TCP实现网络通信(多进程与多线程版本)

时间:2024-11-18 12:25:30

目录

1.TCP实现网络通信的过程

1.1服务端

1.2客户端

2.几个重要函数

2.1 listen()与connect()

2.2 ntohs()与inet_nota

3.基于多进程的TCP通信

3.1 服务端

3.2 客户端

4.基于多线程的TCP通信

4.1 服务端

4.2 客户端

4.3 任务

4.4 线程池


1.TCP实现网络通信的过程

1.1服务端

1.创建网络套接字:socket(),协议族为AF_INET,套接字类型为SOCK_STREAM。

本质上是打开一个套接字文件,目前仅仅有与系统相关的内容(inode和blocks)。该套接字文件主要负责监听。

2.链接网络套接字与本机ip和端口号:bind(),需要建立输出型参数协议地址空间sockaddr_in。

本质上讲本机网络信息内容和套接字文件信息相关联。

3.监听网络连接:listen(),参数为套接字的文件描述符。

本质上是将套接字文件设置为监听状态,允许别人连接。

4.获取新连接到应用层:accept(),返回值为一个新的套接字的文件描述符,参数为监听套接字的文件描述符。

返回的套接字文件主要负责信息的传输。

5.使用read()或者write函数对新套接字文件进行读写,实现网络通信。

6.关闭套接字文件:close()

1.2客户端

1.创建网络套接字:socket(),协议族为AF_INET,套接字类型为SOCK_STREAM。

2.连接服务器:connect()需要建立协议地址空间,并填入客户端的信息。

3.使用read()或者write()对新套接字文件进行读写,实现网络通信。

4.关闭套接字文件close()。

2.几个重要函数

socket等函数以及协议地址空间在udp通信一文中已经说过了。

2.1 listen()与connect()

int listen(int sockfd,int backlog);

第一个参数代表用于监听的套接字文件的文件描述符。监听成功返回0,监听失败返回-1。

int connect(int sockfd,const struct sockaddr*addr,socklen_t addrlen);

第一个参数代表用于监听的套接字文件的文件描述符。第二个参数为服务端的协议地址空间,第三个参数为该协议地址空间的大小。

2.2 ntohs()与inet_nota

uint16_t ntohs(uint16_t netshort);

将一个网络形式的port转化为字符串形式。

char* inet_ntoa(struct in_addr in);

将一个struct in_addr类型的结构体中的s.addr取出,并将该ip转化成点分十进制。

3.基于多进程的TCP通信

3.1 服务端

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include<signal.h>
#include<sys/wait.h>
using namespace std;
void Usage(string proc)
{
    cout << proc << "port_in" << endl;
}
void service(int new_sock)
{
    while (true)
    {
        char buffer[1024];
        memset(buffer, 0, sizeof(buffer));
        ssize_t s = read(new_sock, buffer, sizeof(buffer) - 1);
        if (s > 0)
        {
            buffer[s] = 0;
            cout << "client#" << buffer << endl;
            string echo_string = "server ";
            echo_string += buffer;
            write(new_sock, echo_string.c_str(), echo_string.size());
        }
        else if (s == 0)
        {
            cout << "client quit" << endl;
            break;
        }
        else
        {
            cerr << "read error" << endl;
            break;
        }
    }
}
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        return 1;
    }
    //创建套接字
    int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_sock < 0)
    {
        cerr << "socket error" << errno << endl;
        return 2;
    }
    //绑定套接字
    struct sockaddr_in local;
    memset(&local, 0, sizeof(local));
    local.sin_family = AF_INET;
    local.sin_port = htons(atoi(argv[1]));
    local.sin_addr.s_addr = INADDR_ANY;
    if ((bind(listen_sock, (struct sockaddr *)&local, sizeof(local))) < 0)
    {
        cerr << "bind error" << errno << endl;
        return 3;
    }
    //等待客户端建立链接
    const int back_log = 5;
    if (listen(listen_sock, back_log) < 0)
    {
        cerr << "listen error" << endl;
        return 4;
    }
    //signal(SIGCHLD,SIG_IGN);
    while (true)
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        int new_sock = accept(listen_sock, (struct sockaddr *)&peer, &len);
        if (new_sock < 0)
        {
            continue;
        }
        cout << "get a new link..." ;
        uint16_t client_port=ntohs(peer.sin_port);
        string client_ip=inet_ntoa(peer.sin_addr);
        cout<< client_ip<<":"<<client_port<<endl;
        //提供服务
        pid_t id=fork();
        if(id<0)
        {
            continue;   
        }
        else if(id==0)
        {
            close(listen_sock);
            if(fork()>0) exit(0);
            service(new_sock);
            close(new_sock);
            exit(0);
        }
        else
        {
            waitpid(id,nullptr,0);
            close(new_sock);//do nothing
        }
    }
}

3.2 客户端

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
using namespace std;
void Usage(string proc)
{
    cout<<proc<<"ip_ "<<"port_ "<<endl;
}
int main(int argc,char* argv[])
{
    if(argc!=3)
    {
        Usage(argv[0]);
        return 1;
    }
    int sock=socket(AF_INET,SOCK_STREAM,0);
    string svr_ip=argv[1];
    uint16_t port=atoi(argv[2]);
    if(sock<0)
    {
        cerr<<"socket error"<<endl;
        return 2;
    }
    struct sockaddr_in server;
    bzero(&server,sizeof(server));
    server.sin_family=AF_INET;
    server.sin_addr.s_addr=inet_addr(svr_ip.c_str());
    server.sin_port=htons(port);
    if(connect(sock,(struct sockaddr*)&server,sizeof(server))<0)
    {
        cout<<"connect fail"<<endl;
        return 3;
    }
    cout<<"connect success"<<endl;
    while(true)
    {
        cout<<"please Enter#"<<endl;
        char buffer[1024];
        fgets(buffer,sizeof(buffer)-1,stdin);
        write(sock,buffer,strlen(buffer));
        ssize_t s=read(sock,buffer,sizeof(buffer)-1);
        if(s>0)
        {
            buffer[s]=0;
            cout<<"server echo#"<<buffer<<endl;
        }
    } 
}

4.基于多线程的TCP通信

我们使用线程池来完成多线程实现TCP通信:

4.1 服务端

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include<signal.h>
#include<sys/wait.h>
#include"Threadpool.hpp"
using namespace std;
using namespace ns_threadpool;
void Usage(string proc)
{
    cout << proc << "port_in" << endl;
}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        return 1;
    }
    //创建套接字
    int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_sock < 0)
    {
        cerr << "socket error" << errno << endl;
        return 2;
    }
    //绑定套接字
    struct sockaddr_in local;
    memset(&local, 0, sizeof(local));
    local.sin_family = AF_INET;
    local.sin_port = htons(atoi(argv[1]));
    local.sin_addr.s_addr = INADDR_ANY;
    if ((bind(listen_sock, (struct sockaddr *)&local, sizeof(local))) < 0)
    {
        cerr << "bind error" << errno << endl;
        return 3;
    }
    //等待客户端建立链接
    const int back_log = 5;
    if (listen(listen_sock, back_log) < 0)
    {
        cerr << "listen error" << endl;
        return 4;
    }
    //signal(SIGCHLD,SIG_IGN);
    while (true)
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        int new_sock = accept(listen_sock, (struct sockaddr *)&peer, &len);
        if (new_sock < 0)
        {
            continue;
        }
        cout << "get a new link..." ;
        uint16_t client_port=ntohs(peer.sin_port);
        string client_ip=inet_ntoa(peer.sin_addr);
        cout<< client_ip<<":"<<client_port<<endl;
        //提供服务
        Task t(new_sock);
        ThreadPool<Task>::getinstance()->Push(t);

    }
}

4.2 客户端

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
using namespace std;
void Usage(string proc)
{
    cout<<proc<<"ip_ "<<"port_ "<<endl;
}
int main(int argc,char* argv[])
{
    if(argc!=3)
    {
        Usage(argv[0]);
        return 1;
    }
    int sock=socket(AF_INET,SOCK_STREAM,0);
    string svr_ip=argv[1];
    uint16_t port=atoi(argv[2]);
    if(sock<0)
    {
        cerr<<"socket error"<<endl;
        return 2;
    }
    struct sockaddr_in server;
    bzero(&server,sizeof(server));
    server.sin_family=AF_INET;
    server.sin_addr.s_addr=inet_addr(svr_ip.c_str());
    server.sin_port=htons(port);
    if(connect(sock,(struct sockaddr*)&server,sizeof(server))<0)
    {
        cout<<"connect fail"<<endl;
        return 3;
    }
    cout<<"connect success"<<endl;
    while(true)
    {
        cout<<"please Enter#"<<endl;
        char buffer[1024];
        fgets(buffer,sizeof(buffer)-1,stdin);
        write(sock,buffer,strlen(buffer));
        ssize_t s=read(sock,buffer,sizeof(buffer)-1);
        if(s>0)
        {
            buffer[s]=0;
            cout<<"server echo#"<<buffer<<endl;
        }
    } 
}

4.3 任务

#include <iostream>
#include<string.h>
#include<unistd.h>
using namespace std;
namespace ns_task
{
    struct Task
    {
    private:
        int sock_;
    public:
        Task() : sock_(-1) {}
        Task(int sock) : sock_(sock) {}
        int Run()
        {
            while (true)
            {
                char buffer[1024];
                memset(buffer, 0, sizeof(buffer));
                ssize_t s = read(sock_, buffer, sizeof(buffer) - 1);
                if (s > 0)
                {
                    buffer[s] = 0;
                    cout << "client#" << buffer << endl;
                    string echo_string = "server ";
                    echo_string += buffer;
                    write(sock_, echo_string.c_str(), echo_string.size());
                }
                else if (s == 0)
                {
                    cout << "client quit" << endl;
                    break;
                }
                else
                {
                    cerr << "read error" << endl;
                    break;
                }
            }
            close(sock_);
        }
    };
}

4.4 线程池

#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <queue>
#include "task.hpp"
using namespace std;
using namespace ns_task;
namespace ns_threadpool
{
    template <class T>
    class ThreadPool
    {
    private:
        int num_;
        pthread_mutex_t mtx;
        pthread_cond_t cond;
        queue<T> Task;
        static ThreadPool<T>* ins;
    public:
        ThreadPool() : num_(5)
        {
            pthread_mutex_init(&mtx,nullptr);
            pthread_cond_init(&cond,nullptr);
        }
        void Lock()
        {
            pthread_mutex_lock(&mtx);
        }
        void unLock()
        {
            pthread_mutex_unlock(&mtx);
        }
        void Wait()
        {
            pthread_cond_wait(&cond, &mtx);
        }
        void Wakeup()
        {
            pthread_cond_signal(&cond);
        }
        void Push(T &task)
        {
            Lock();
            Task.push(task);
            unLock();
            Wakeup();
        }
        void Pop(T *task)
        {
            *task = Task.front();
            Task.pop();
        }
        bool IsEmpty()
        {
            return Task.empty();
        }
        static void *Routine(void *args)
        {
            pthread_detach(pthread_self());
            ThreadPool<T> *tp = (ThreadPool<T> *)args;
            while (true)
            {
                tp->Lock();
                if (tp->IsEmpty())
                {
                    tp->Wait();
                }
                T t;
                tp->Pop(&t);
                tp->unLock();
                t.Run();
            }
        }
        void InitThreadPool()
        {
            pthread_t tid;
            for (int i = 0; i < num_; i++)
            {
                pthread_create(&tid,nullptr, Routine, (void *)this);
            }
        }
        static ThreadPool<T>* getinstance()
        {
            if(ins==nullptr)
            {
                pthread_mutex_t mmtx=PTHREAD_MUTEX_INITIALIZER;
                pthread_mutex_lock(&mmtx);
                if(ins==nullptr)
                {
                    ins=new ThreadPool<T>();
                    ins->InitThreadPool();
                }
                pthread_mutex_unlock(&mmtx);
            }
            return ins;
        }
        ~ThreadPool()
        {
            pthread_mutex_destroy(&mtx);
            pthread_cond_destroy(&cond);
        }
    };
    template<class T>
    ThreadPool<T>* ThreadPool<T>::ins=nullptr;
}