tcp通信-多线程、多进程

多进程 - 所有的代码没有写完安全处理, 初学阶段

初学阶段:看到一篇大佬的博客,写得很好,帮助我理解send、recv接口和接收缓冲区、发送缓冲区之间的关系。 下面引用大佬的一些话:
       每个TCP socket在内核中都有一个发送缓冲区和一个接收缓冲区,TCP的全双工的工作模式以及TCP的流量(拥塞)控制便是依赖于这两个独立的buffer以及buffer的填充状态。接收缓冲区把数据缓存入内核,应用进程一直没有调用recv()进行读取的话,此数据会一直缓存在相应socket的接收缓冲区内。再啰嗦一点,不管进程是否调用recv()读取socket,对端发来的数据都会经由内核接收并且缓存到socket的内核接收缓冲区之中。recv()所做的工作,就是把内核缓冲区中的数据拷贝到应用层用户的buffer里面,并返回,仅此而已。进程调用send()发送的数据的时候,最简单情况(也是一般情况),将数据拷贝进入socket的内核发送缓冲区之中,然后send便会在上层返回。换句话说,send()返回之时,数据不一定会发送到对端去(和write写文件有点类似),send()仅仅是把应用层buffer的数据拷贝进socket的内核发送buffer中,发送是TCP的事情,和send其实没有太大关系。
原文链接:https://blog.csdn.net/a879365197/article/details/72802364

tcpser.hpp

  1 #include<stdio.h>                                                                                                                           
  2 #include<iostream>
  3 #include<sys/socket.h>
  4 #include<string>
  5 #include<string.h>
  6 #include<netinet/in.h>
  7 #include<unistd.h>
  8 #include<arpa/inet.h>
  9 #include<stdlib.h>
 10 
 11 //封装tcpert类
 12 class TcpSer{
 13     public:
 14         
 15         //重命名 - 方便使用
 16         typedef struct sockaddr_in sockaddr_in;
 17         typedef struct sockaddr sockaddr;
 18         
 19         //构造函数
 20         TcpSer()
 21             :sockfd_(-1)
 22         {
 23 
 24         }
 25         
 26         //析构函数
 27         ~TcpSer(){
 28 
 29 
 30         }       
 31 
 32         //socket接口 - ipv4, tcp, 字节流服务
 33         bool Socket(){
 34             
 35             sockfd_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 36             if(sockfd_ < 0){
 37                 perror("socket!\n");
 38                 return false;
 39             }
 40 
 41             return true;
 42         }
 43         
 44         //bind接口 - 服务器使用
 45         bool Bind(const std::string& IP, uint16_t PORT){
 46             
 47             //创建struct sockaddr_in类存放地址信息                                                                                          
 48             sockaddr_in addr;
 49             addr.sin_family = AF_INET;
 50             addr.sin_port = htons(PORT);
 51             addr.sin_addr.s_addr = inet_addr(IP.c_str());
 52             
 53             int ret = bind(sockfd_, (sockaddr*)&addr, sizeof(addr));
 54             if(ret < 0){
 55                 perror("bind!");
 56                 return false;
 57             }
 58 
 59             return true;
 60         }
 61         
 62         //connect接口 - 客户端使用
 63         bool Connect(const char* IP, uint16_t PORT){
 64             
 65            //创建struct sockaddr_in类存放地址信息 
 66            sockaddr_in peer;
 67            peer.sin_family = AF_INET;
 68            peer.sin_port = htons(PORT);
 69            peer.sin_addr.s_addr = inet_addr(IP);
 70             
 71            int ret = connect(sockfd_, (sockaddr*)&peer, sizeof(peer));
 72            if(ret < 0){
 73                perror("connect!");                                                                                                          
 74                return false;
 75            } 
 76 
 77            return true;
 78         }
 79         
 80         //listen接口 - 服务器使用
 81         bool Listen(const int backlog = 5){
 82             
 83             int ret = listen(sockfd_, backlog);
 84             if(ret < 0){
 85                 perror("listen!");
 86                 return false;
 87             }
 88 
 89             return true;
 90         }   
 91 
 92         //accept接口 - 服务器使用 - 需要接受用户端的地址信息 - 
 93         //采用出参保存, 还需要创建TcpSer类来保留交互的fd
 94         bool Accept(TcpSer* ts, sockaddr_in& peeraddr){
 95             
 96             socklen_t addrlen = sizeof(peeraddr);
 97             
 98             int ret = accept(sockfd_, (sockaddr*)&peeraddr, &addrlen);
 99             if(ret < 0){                                                                                                                    
100                 perror("accept!");
101                 return false;
102             }
103            
104             ts->sockfd_ = ret;
105             
106             return true;
107         }
108         
109         //send接口
110         bool Send(const std::string& Buf){
111             
112             int write_size = send(sockfd_, Buf.c_str(), Buf.size(), 0);
113             
114             if(write_size < 0){
115                 perror("send!");
116                 return false;
117             }
118 
119             return true;
120         }
121         
122         //recv接口
123         bool Recv(std::string& Buf){
124              
125             char buf[1024 * 10] = {0};                                                                                                      
126 
127             int read_size = recv(sockfd_, buf , sizeof(buf) - 1, 0);
128          
129             if(read_size < 0){
130                 perror("read_size!");
131                 return false;
132             }
133             else if(0 == read_size){
134                 printf("peer close this connect!\n");
135                 return false;
136             }
137 
138             Buf.assign(buf, read_size);
139             return true;
140         }
141         
142         //释放fd接口 - 系统调用函数
143         void Close(){
144             close(sockfd_);
145             sockfd_ = -1;
146         }
147     private:
148         int sockfd_;
149 };                                      

多进程实现服务器端

  1 #include"tcpser.hpp"                                                                                                                        
  2 #include<signal.h>
  3 #include<sys/wait.h>
  4 
  5 //使用更改信号默认处理函数 - wait回收子进程资源。
  6 void sigcb(int signum){
  7     (void)signum;
  8     wait(NULL);
  9 }
 10 
 11 int main(int argc, char* argv[]){
 12 	//使用命令行参数来赋值ip、port    
 13     if(argc != 3){
 14         printf("未输入参数IP&PORT!\n");
 15         return 0;
 16     }
 17     
 18     //更该SIG_CHLD17信号的处理方式 - 防止僵尸进程
 19     signal(17, sigcb);
 20     //IP、			    
 21     uint16_t PORT = atoi(argv[2]);
 22     std::string IP = argv[1];
 23     
 24     TcpSer ts;
 25 	//socket
 26     if(!ts.Socket()){
 27         perror("socket failed!");
 28         return 0;
 29     }
 30     //bind 绑定地址信息		    
 31     if(!ts.Bind(IP, PORT)){
 32         perror("bind failed!");
 33         return 0;
 34     }
 35     //listen 监听接口
 36     if(!ts.Listen()){
 37         perror("Listen!");
 38         return 0;
 39     }
 40     
 41     printf("服务器端已启动!\n");
 42 	//服务器处理主流程
 43     while(1){
 44         //申请存放用户交互的fd类 和 客户端地址信息
 45         TcpSer* peerts = new TcpSer;
 46         sockaddr_in peeraddr;
 47         //获得三次握手的请求并返回交互的fd                                                                                                                                    
 48         if(!ts.Accept(peerts, peeraddr)){
 49             perror("accept!\n");
 50             return 0;
 51         }
 52         //进程处理流程
 53         int ret = fork();
 54         
 55         if(ret < 0){	//子进程创建失败
 56             perror("fork!");
 57             return 0;
 58         }
 59         else if(0 == ret){
 60 			//子进程处理流程
 61             printf("ser have a new connect, ip:port ---> %s:%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
 62             
 63             while(1){
 64                 std::string Buf;
 65         
 66                 if(!peerts->Recv(Buf)){
 67                     continue;
 68                 }   
 69         
 70                 printf("client respond: [%s]\n", Buf.c_str());
 71        
 72                 printf("serve respond:");
 73                 fflush(stdout);                                                                                                             
 74                 std::getline(std::cin, Buf);
 75         
 76                 if(!peerts->Send(Buf)){
 77                     continue;
 78                 }
 79             }
 80            	//交互完毕之后,释放peerts资源 和 交互的fd        
 81             peerts->Close();
 82             delete peerts;
 83             exit(0);
 84         }
 85 
 86     }
 87     //主进程释放监听的fd
 88     ts.Close();
 89     return 0;
 90 }              

多线程实现服务器端

  1 /*                                                                                                                                          
  2     //采用多线程实现的服务器 
  3 */
  4                                                                 
  5 #include"tcpser.hpp"                     
  6 #include<pthread.h>             
  7                      
  8          
  9 //线程入口函数
 10 void* thread_start(void* arg)                                                                                         
 11 {
 12     //线程分离 - 结束后由os释放内存资源
 13     pthread_detach(pthread_self());
 14                                                                           
 15     //获取操作的类 - ts
 16     TcpSer* ts = (TcpSer*)arg;
 17                                       
 18                      
 19     //服务器端执行流程
 20     while(1){
 21         std::string Buf;
 22 
 23         //recv接口 - 从接受缓冲区读取数据
 24         if(!ts->Recv(Buf))
 25             continue;
 26         printf("client says: [%s]\n", Buf.c_str());
 27     
 28         printf("serve respond:");
 29         fflush(stdout);
 30         std::getline(std::cin, Buf);
 31         
 32         //send接口 - 向发送缓冲区发送数据
 33         if(!ts->Send(Buf))
 34             continue;
 35 
 36         printf("发送数据成功!\n");
 37     }
 38     
 39     //关闭socketfd
 40     ts->Close();
 41     //这块的ts是从堆上开辟的, 我们让每个线程自己取释放, 可以事半功倍!
 42     delete ts;      //释放开辟的内存 -- 很关键
 43 }
 44 
 45 
 46 //服务器端的主流程
 47 int main(int argc, char* argv[]){                                                                                                           
 48     
 49     //接受IP、PORT - 未提供接口获得,简单测试
 50     if(argc != 3){
 51         printf("未输入参数IP&PORT!\n");
 52         return 0;
 53     }
 54     
 55     //IP、 PORT
 56     uint16_t PORT = atoi(argv[2]);
 57     std::string IP = argv[1];
 58     
 59 
 60     //监听ts
 61     TcpSer ts;
 62     
 63     //socket
 64     if(!ts.Socket()){
 65         perror("socket failed!");
 66         return 0;
 67     }
 68      
 69     //bind - 绑定信息
 70     if(!ts.Bind(IP, PORT)){
 71         perror("bind failed!");
 72         return 0;
 73     }                                                                                                                                       
 74     
 75     //listen - 监听接口, 由内核维护这块代码 - 当传入新的三次握手成功的请求,就会将该请求放到完成的队列当中
 76     //等到accept接口获取。
 77     if(!ts.Listen()){
 78         perror("Listen!");
 79         return 0;
 80     }
 81     
 82     printf("服务器端已启动!\n");
 83     
 84     //serve主线程流程
 85     while(1){
 86         
 87         //堆上开辟peerts - 操控peerts->ts
 88         TcpSer* peerts = new TcpSer;
 89 
 90         //保留客户端的地址信息 - 以出参形式
 91         sockaddr_in peeraddr;
 92 
 93         //accept接口, 获取三次握手的请求,并返回新的操作句柄-fd
 94         if(!ts.Accept(peerts, peeraddr)){
 95             perror("accept!\n");
 96             return 0;
 97         }
 98         
 99         printf("ser has new connection - ip[%s], port[%d]\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));                      
100         
101         //调用线程执行流程
102         pthread_t tid;
103         int ret = pthread_create(&tid, NULL, thread_start, (void*)peerts);
104     
105         if(ret){
106             perror("pthread_create!");
107             return 0;
108         }
109 
110     }
111     
112     //释放监听socket
113     ts.Close();
114     return 0;
115 }                                                  

以上是简单实现多进程、多线程tcp通信, 里面还有很多缺点(例如:采用while死循环无法正常结束等等。)

发布了25 篇原创文章 · 获赞 16 · 访问量 919

猜你喜欢

转载自blog.csdn.net/weixin_44024891/article/details/105125177