Linux——套接字编程(二):TCP的socket编程(流程,接口),TCP 客户端 / 服务端通信(单进程版本,多进程版本,多线程版本)

文章目录:

  • 1. TCP的socket编程(流程&接口)
    • 1.1 编程流程
    • 1.2 TCP的socket编程接口
      • 1.2.1 服务端监听接口
      • 1.2.2 服务端获取新连接
      • 1.2.3 客户端发起连接
      • 1.2.4 发送接口
      • 1.2.4 接收接口
  • 2. 判断端口是否侦听
  • 3. 连接建立的现象
  • 4. TCP 客户端 / 服务端(单进程实现)
  • 5. TCP单进程版本的问题
  • 6. TCP 客户端 / 服务端(多进程实现)
  • 7. TCP 客户端 / 服务端(多线程实现)


1. TCP的socket编程(流程&接口)

1.1 编程流程

Linux——套接字编程(二):TCP的socket编程(流程,接口),TCP 客户端 / 服务端通信(单进程版本,多进程版本,多线程版本)_第1张图片

1.2 TCP的socket编程接口

创建套接字接口socket(),绑定端口bind(),关闭套接字接口close(),的使用和UDP套接字编程中的使用是一样的,此处不再提及

1.2.1 服务端监听接口

int listen(int sockfd, int backlog);

  • sockfd:套接字描述符
  • backlog:已完成连接队列的大小
  • 返回值
      成功:0
      失败:-1

当客户端和服务端进行三次握手的时候会存在两种状态连接还未建立连接已建立,此时操作系统内核中就会存在两个队列未完成连接队列已完成连接队列

如下图所示:
Linux——套接字编程(二):TCP的socket编程(流程,接口),TCP 客户端 / 服务端通信(单进程版本,多进程版本,多线程版本)_第2张图片

如上图若客户端只完成①或①②则此连接在未完成连接队列中,当完成三次握手后会由未完成连接队列放到已完成连接队列

backlog就是已完成连接队列的大小,backlog影响了服务端并发接收连接的能力

eg:假设backlog=1,服务端不accepct接收连接,此时有三个客户端都完成了三次握手,则必有一个客户端连接进入已完成连接队列中,由于已完成连接队列空间不够,所以剩余两个客户端的连接只能放入未完成连接队列中

1.2.2 服务端获取新连接

从已经完成连接队列中获取已经完成三次握手的连接,没有连接时,调用accept会阻塞

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

  • sockfd:套接字描述符(listen_sockfd)
  • addr:客户端地址信息结构(客户端IP,客户端的端口)
  • addrlen:客户端地址信息结构的长度
  • 返回值
      成功:返回新连接的套接字描述符
      失败:返回-1

三次握手的时候是对listen_sockfd进行操作,当调用accept()会在Tcp服务端内部创建一个新的套接字new_sockfd,三次握手之后的数据收发都是对new_sockfd进行操作,如下图所示:
Linux——套接字编程(二):TCP的socket编程(流程,接口),TCP 客户端 / 服务端通信(单进程版本,多进程版本,多线程版本)_第3张图片

1.2.3 客户端发起连接

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

  • sockfd:套接字描述符(listen_sockfd)
  • addr:服务端地址信息结构(服务端IP,服务端的端口)
  • addrlen:服务端地址信息结构的长度
  • 返回值
      成功:返回0
      小于0,连接失败

1.2.4 发送接口

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

  • sockfd:套接字描述符(new_sockfd)
  • buf:待要发送的数据
  • len:发送数据的长度
  • flags
      0:阻塞发送
      MSG_OOB:发送带外数据
  • 返回值
      大于0:返回发送的字节数量
      -1:发送失败

带外数据:即在紧急情况下所产生的数据,会越过前面进行排队的数据优先进行发送

1.2.4 接收接口

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

  • sockfd:套接字描述符(new_sockfd)
  • buf:将接收的数据放到buf
  • len:buf的最大接收能力
  • flags:0:阻塞发送;如果客户端没有发送数据,调用recv会阻塞
  • 返回值
      大于0:正常接收了多少字节数据
      等于0:对端将连接关闭了
      小于0:接受失败

2. 判断端口是否侦听

写一个Tcp服务端代码,让其创建套接字,然后绑定地址信息,然后监听,最后写一个while死循环方便我们看现象,代码如下:

  1 #include<stdio.h>                                                                                                            
  2 #include<unistd.h>
  3 #include<sys/socket.h>
  4 #include<arpa/inet.h>
  5 
  6 int main()
  7 {
  8     int listen_sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
  9     if(listen_sockfd < 0)
 10     {
 11         perror("socket");
 12         return 0;
 13     }
 14 
 15     struct sockaddr_in addr;
 16     addr.sin_family = AF_INET;
 17     addr.sin_port = htons(18989);
 18     addr.sin_addr.s_addr = inet_addr("0.0.0.0");
 19     int ret = bind(listen_sockfd,(struct sockaddr*)& addr,sizeof(addr));
 20     if(ret < 0)
 21     {
 22         perror("bind");
 23         return 0;
 24     }
 25 
 26     ret = listen(listen_sockfd,1);
 27     if(ret < 0)             
 28     {                         
 29         perror("listen");        
 30         return 0;                               
 31     }                                                                   
 32                
 33     while(1)
 34     {                  
 35         sleep(1);
 36     }
 37     return 0;
 38 }                                              

让如上代码跑起来,我们应该看到程序在sleep,端口状态为监听状态,如下图所示:
Linux——套接字编程(二):TCP的socket编程(流程,接口),TCP 客户端 / 服务端通信(单进程版本,多进程版本,多线程版本)_第4张图片

3. 连接建立的现象

我们可以用windows下的工具telnet模仿TCP三次握手建立连接,在windows下的cmd窗口输入 “tenlet + 公网IP + 端口号” 即可模拟测试

测试如下:

首先我们让2中的服务端程序跑起来,然后在cmd中使用telnet进行测试
Linux——套接字编程(二):TCP的socket编程(流程,接口),TCP 客户端 / 服务端通信(单进程版本,多进程版本,多线程版本)_第5张图片
回车后若如下图所示,则建立连接成功
Linux——套接字编程(二):TCP的socket编程(流程,接口),TCP 客户端 / 服务端通信(单进程版本,多进程版本,多线程版本)_第6张图片
当我们使用telnet与服务器建立三次连接(即进行三次三次握手)我们会看到,当我们查看服务器端口的使用情况是时会看到如下情况:
Linux——套接字编程(二):TCP的socket编程(流程,接口),TCP 客户端 / 服务端通信(单进程版本,多进程版本,多线程版本)_第7张图片
虽然我们在代码中将已完成连接队列的大小设为1,但上图已完成连接队列中却放了两个已完成连接,正常情况当我们就backlog设为1,已完成连接队列中只能放一个已完成连接,那么为什么会出现种情况呢?原因是操作系统内核中判断已完成队列是否已满的逻辑是如下所示:

 if(queue.size > backlog)
{
    //不再往已完成连接队列中放
    //不再建立连接                                                                                                         
}     

所以我们设置的bakclog=1,向已完成连接队列中放入一个已完成连接,queue.size= 1不大于backlog=1,所以再向已完成连接队列中放入一个已完成连接,此时queue.size= 2大于backlog=1,不再放入,所以就出现如上图所示现象,虽然我们将backlog设为1,但已完成连接队列中却有两个已完成连接。

4. TCP 客户端 / 服务端(单进程实现)

客户端主要流程:创建套接字,发起连接,发送和接收

客户端代码如下:

  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<unistd.h>
  4 #include<sys/socket.h>
  5 #include<arpa/inet.h>
  6 
  7 int main()
  8 {
  9     int sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
 10     if(sockfd < 0)
 11     {
 12         perror("socket");
 13         return 0;
 14     }
 15 
 16     struct sockaddr_in addr;
 17     addr.sin_family = AF_INET;
 18     addr.sin_port = htons(18989);
 19     addr.sin_addr.s_addr = inet_addr("0.0.0.0");                                                                             
 20     int ret = connect(sockfd,(struct sockaddr*)&addr,sizeof(addr));
 21     if(ret < 0)
 22     {
 23         perror("connect");
 24         return 0;
 25     }
 26 
 27 
 28     while(1)
 29     {
 30 
 31         sleep(1);
 32 
 33        char buf[1024] = {0};
 34        sprintf(buf,"hello server,i am client\n");
 35        ssize_t send_ret = send(sockfd,buf,strlen(buf),0);
 36        if(send_ret < 0)
 37        {
 38            perror("send");
 39            continue;
 40        }
 41 
 42 
 43        memset(buf,'\0',sizeof(buf));
 44        ssize_t recv_ret = recv(sockfd,buf,sizeof(buf)-1,0);
 45        if(recv_ret < 0)
 46        {                                                                                                                     
 47            perror("recv");
 48            continue;
 49        }
 50        else if(recv_ret == 0)
 51        {
 52            printf("server close");
 53 
 54            close(sockfd);
 55 
 56            return 0;
 57        }
 58 
 59        printf("server say: %s\n",buf);
 60     }
 61 
 62     close(sockfd);
 63     return 0;
 64 }                                                 

服务端主要流程:创建侦听套接字,绑定地址信息,监听,接收新连接,接收,发送

服务端代码如下:

  1 #include<stdio.h>                                                                                                            
  2 #include<string.h>            
  3 #include<unistd.h>
  4 #include<sys/socket.h>             
  5 #include<arpa/inet.h>         
  6                                  
  7 int main()           
  8 {        
  9     int listen_sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
 10     if(listen_sockfd < 0)                          
 11     {
 12         perror("socket");            
 13         return 0; 14     }
 15                                                               
 16     struct sockaddr_in addr;
 17     addr.sin_family = AF_INET;
 18     addr.sin_port = htons(18989);
 19     addr.sin_addr.s_addr = inet_addr("0.0.0.0");
 20     int ret = bind(listen_sockfd,(struct sockaddr*)&addr,sizeof(addr));
 21     if(ret < 0)
 22     {
 23         perror("bind");
 24         return 0;        
 25     }        
 26  
 27     ret = listen(listen_sockfd,1);
 28     if(ret < 0)
 29     {
 30         perror("listen");
 31         return 0;
 32     }
 33 
 34     struct sockaddr_in peer_addr;
 35     socklen_t socklen = sizeof(peer_addr);
 36     int new_sockfd = accept(listen_sockfd,(struct sockaddr*)&peer_addr,&socklen);
 37     if(new_sockfd < 0)
 38     {
 39         perror("accept");
 40         return 0;
 41     }
 42 
 43     while(1)
 44     {
 45         char buf[1024] = {0};
 46         ssize_t recv_ret = recv(new_sockfd,buf,sizeof(buf)-1,0);
 47         if(recv_ret < 0)                                                                                                     
 48         {
 49             perror("recv");
 50             continue;
 51         }
 52         else if(recv_ret == 0)
 53         {
 54             printf("client close");
 55             close(new_sockfd);
 56             close(listen_sockfd);
 57             return 0;
 58         }
 59 
 60         printf("client%d say:%s%d",new_sockfd,buf,new_sockfd);
 61 
 62         memset(buf,'\0',sizeof(buf));
 63         sprintf(buf,"hello,i am server,i recv client ip is %s,port is %d",inet_ntoa(peer_addr.sin_addr),ntohs(peer_addr.sin_port));
 64         ssize_t send_ret = send(new_sockfd,buf,strlen(buf),0);
 65         if(send_ret < 0)
 66         {
 67             perror("send");
 68             continue;
 69         }
 70 
 71     }
 72     close(new_sockfd);
 73     close(listen_sockfd);
 74     return 0;
 75 }                                                                                                 

运行结果如下:

客户端
Linux——套接字编程(二):TCP的socket编程(流程,接口),TCP 客户端 / 服务端通信(单进程版本,多进程版本,多线程版本)_第8张图片

服务端
Linux——套接字编程(二):TCP的socket编程(流程,接口),TCP 客户端 / 服务端通信(单进程版本,多进程版本,多线程版本)_第9张图片

5. TCP单进程版本的问题

如上4中所示,TCP单进程版本运行结果一切都符合预期,但如果再来一个客户端和服务端进行通信会是什么结果呢?

如下图所示:
Linux——套接字编程(二):TCP的socket编程(流程,接口),TCP 客户端 / 服务端通信(单进程版本,多进程版本,多线程版本)_第10张图片
如上图所示,我们可以看到第二个客户端虽然跑起来了,但没有输出来自服务端发送的数据,这是为什么呢?
在这里插入图片描述
通过pstack查看客户端阻塞在recv处,因为服务端accept接收新连接在while循环外面,所以服务端在进行一次连接之后会进入while循环内部,不能再接收新连接(虽然客户端2和服务端完成了三次握手建立了新连接,但服务端无法接收连接,此时客户端则无法收到服务端的数据)

若将accept放入while循环里呢?

将accpect放入while循环中,则每个客户端只能收到一条,当客户端与服务端建立连接,向服务端发送数据服务端,服务端接收数据并回复客户端,此时服务端将回到while循环的开始阻塞在accept处(因为之前已经接收客户端发起的连接,当第二次accept时,已完成连接队列中就是空队列)

TCP单进程存在的问题:当存在多个客户端与服务器进行通信时,可能会出现recv阻塞accept阻塞

解决办法:
    ①使用多进程
    ②使用多线程
    ③使用多路转接的技术

6. TCP 客户端 / 服务端(多进程实现)

  多进程的客户端代码和单进程是一样的,单进程服务端父进程负责accept,子进程负责数据的接收和发送,需要注意的是,父进程一定要进程等待,防止子进程先于父进程退出使子进程变为僵尸进程,而父进程不能直接父进程的逻辑处使用wait或waitpid进行等待,因为阻塞等待,若子进程一直不退出,则父进程一直在等待,永远无法接收新连接,我们 需要使用需要使用自定义信号处理方式将SIGCHLD信号重新定义,当子进程退出发出SIGCHLD信号时,父进程则对子进程的资源进行回收

客户端主要流程:创建套接字,发起连接,发送和接收

客户端代码如下:

  1 #include<stdio.h>                                                                                    
  2 #include<string.h>
  3 #include<unistd.h>
  4 #include<sys/socket.h>
  5 #include<arpa/inet.h>
  6 
  7 int main()
  8 {
  9     int sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
 10     if(sockfd < 0)
 11     {
 12         perror("socket");
 13         return 0;
 14     }
 15 
 16     struct sockaddr_in addr;
 17     addr.sin_family = AF_INET;
 18     addr.sin_port = htons(18989);
 19     addr.sin_addr.s_addr = inet_addr("0.0.0.0");
 20     int ret = connect(sockfd,(struct sockaddr*)&addr,sizeof(addr));
 21     if(ret < 0)
 22     {
 23         perror("connect");
 24         return 0;
 25     }
 26 
 27 
 28     while(1)
 29     {
 30 
 31         sleep(1);
 32 
 33        char buf[1024] = {0};
 34        sprintf(buf,"hello server,i am client1");
 35        ssize_t send_ret = send(sockfd,buf,strlen(buf),0);
 36        if(send_ret < 0)
 37        {                                                                                             
 38            perror("send");
 39            continue;
 40        }
 41 
 42 
 43        memset(buf,'\0',sizeof(buf));
 44        ssize_t recv_ret = recv(sockfd,buf,sizeof(buf)-1,0);
 45        if(recv_ret < 0)
 46        {
 47            perror("recv");
 48            continue;
 49        }
 50        else if(recv_ret == 0)
 51        {
 52            printf("server close");
 53 
 54            close(sockfd);
 55 
 56            return 0;
 57        }
 58                                                                                                      
 59        printf("server say: %s\n",buf);
 60     }
 61 
 62     close(sockfd);
 63     return 0;
 64 }                                                                                                    

服务端主要流程:创建侦听套接字,绑定地址信息,监听,接收新连接,创建子进程,接收,发送

服务端代码如下:

    1 #include<stdio.h>                                                                                  
    2 #include<string.h>           
    3 #include<unistd.h>
    4 #include<sys/socket.h>                
    5 #include<arpa/inet.h>
    6 #include<signal.h>                         
    7 #include<sys/wait.h>                  
    8                              
    9                  
   10 void sigcallback(int signo)
   11 {
   12     wait(NULL);
   13 }
   14                       
   15 int main()                                   
   16 {                                                         
   18     signal(SIGCHLD,sigcallback);                                      
   19                                 
   20                  
   21     int listen_sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
   22     if(listen_sockfd < 0)
   23     {
   24         perror("socket");
   25         return 0;
   26     }
   27 
   28     struct sockaddr_in addr;
   29     addr.sin_family = AF_INET;
   30     addr.sin_port = htons(18989);
   31     addr.sin_addr.s_addr = inet_addr("0.0.0.0");
   32     int ret = bind(listen_sockfd,(struct sockaddr*)&addr,sizeof(addr));
   33     if(ret < 0)
   34     {
   35         perror("bind");
   36         return 0;
   37     }                                                                                              
   38 
   39     ret = listen(listen_sockfd,1);
   40     if(ret < 0)
   41     {
   42         perror("listen");
   43         return 0;
   44     }
   45 
   46 
   47     while(1)
   48     {
   49         //接收连接
   50         struct sockaddr_in peer_addr;
   51         socklen_t socklen = sizeof(peer_addr);
   52         int new_sockfd = accept(listen_sockfd,(struct sockaddr*)&peer_addr,&socklen);
   53         if(new_sockfd < 0)
   54         {
   55             perror("accept");
   56             return 0;
   57         }
   58                                                                                                    
   59         pid_t f_ret = fork();
   60         if(f_ret < 0)
   61         {
   62             perror("fork");
   63             continue;
   64         }
   65         else if(f_ret == 0)
   66         {
   67             close(listen_sockfd);
   68             //child
   69 
   70             while(1)
   71             {
   72                 //接收
   73                 char buf[1024] = {0};
   74                 ssize_t recv_ret = recv(new_sockfd,buf,sizeof(buf)-1,0);
   75                 if(recv_ret < 0)
   76                 {
   77                     perror("recv");
   78                     continue;
   79                 }                                                                                  
   80                 else if(recv_ret == 0)
   81                 {
   82                     printf("client close");
   83                     close(new_sockfd);
   84                     return 0;
   85                 }
   86 
   87                 printf("client new_sockfd:%d say:%s\n",new_sockfd,buf);
   88 
   89 
   90                 //发送
   91                 memset(buf,'\0',sizeof(buf));
   92                 sprintf(buf,"hello,i am server,i recv client%d ip is %s,port is %d",new_sockfd,inet      _ntoa(peer_addr.sin_addr),ntohs(peer_addr.sin_port));
   93                 ssize_t send_ret = send(new_sockfd,buf,strlen(buf),0);
   94                 if(send_ret < 0)
   95                 {
   96                     perror("send");
   97                     continue;
   98                 }
   99                                                                                                    
  100             }
  101         }
  102         else
  103         {
  104             //father
  105             //防止客户端关闭,子进程直接return 0退出产生僵尸进程
  106             //所以父进程需要进行等待,但不能在此次使用wait或waitpid
  107             //因为阻塞等待,若子进程一直不退出,则父进程一直在等待,永远无法接收新连接
  108             //需要使用自定义信号处理方式
  109             close(new_sockfd);
  110             continue;
  111         }
  112     }
  113     return 0;
  114 }                                                                                                  

运行结果如下:

客户端1:
Linux——套接字编程(二):TCP的socket编程(流程,接口),TCP 客户端 / 服务端通信(单进程版本,多进程版本,多线程版本)_第11张图片
客户端2:
Linux——套接字编程(二):TCP的socket编程(流程,接口),TCP 客户端 / 服务端通信(单进程版本,多进程版本,多线程版本)_第12张图片
服务端:
Linux——套接字编程(二):TCP的socket编程(流程,接口),TCP 客户端 / 服务端通信(单进程版本,多进程版本,多线程版本)_第13张图片

7. TCP 客户端 / 服务端(多线程实现)

Tcp_thread_server.hpp 服务端接口封装(声明定义可以放一起):

  1 #pragma once                                                                                         
  2 #include<stdio.h>              
  3 #include<string>          
  4 #include<string.h>
  5 #include<unistd.h>                
  6 #include<sys/socket.h>
  7 #include<arpa/inet.h>                  
  8 #include<signal.h>
  9 #include<sys/wait.h>      
 10 #include<pthread.h>
 11 
 12 using namespace std;        
 13          
 14 class TcpSocket
 15 {             
 16     public:                             
 17          
 18         TcpSocket():sockfd_(-1)                                        
 19         {}                  
 20         ~TcpSocket()
 21         {}                     
 22 
 23         void SetSockfd(int sockfd)
 24         {
 25             sockfd_ = sockfd;
 26         }
 27         //创建套接字
 28         int Socket()
 29         {
 30             sockfd_ = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
 31             if(sockfd_ < 0)
 32             {
 33                 perror("socket");
 34                 return -1;
 35             }
 36             return sockfd_;
 37         }                                                                                            
 38 
 39         //绑定地址信息
 40         int Bind(const string& ip = "0.0.0.0",uint16_t port = 18989)
 41         {
 42             struct sockaddr_in addr;
 43             addr.sin_family = AF_INET;
 44             addr.sin_port = htons(port);
 45             addr.sin_addr.s_addr = inet_addr(ip.c_str());
 46             int ret = bind(sockfd_,(struct sockaddr*)&addr,sizeof(addr));
 47             if(ret < 0)
 48             {
 49                 perror("bind");
 50                 return -1;
 51             }
 52             return ret;
 53         }
 54 
 55         //侦听
 56         int Listen(int backlog = 5)
 57         {
 58             int ret = listen(sockfd_,backlog);                                                       
 59             if(ret < 0)
 60             {
 61                 perror("listen");
 62                 return -1;
 63             }
 64             return ret;
 65         }
 66 
 67         //接收新连接
 68         int Accept(struct sockaddr_in* addr)
 69         {
 70             socklen_t socklen = sizeof(struct sockaddr_in);
 71             int new_sockfd = accept(sockfd_,(struct sockaddr*)addr,&socklen);
 72             if(new_sockfd < 0)
 73             {
 74                 perror("accept");
 75                 addr = NULL;
 76                 return -1;
 77             }
 78             return new_sockfd;
 79         }                                                                                            
 80 
 81         //接收
 82         ssize_t Recv(string* data)
 83         {
 84             data->clear();
 85             char buf[1024] = {0};
 86             ssize_t recv_ret = recv(sockfd_,buf,sizeof(buf)-1,0);
 87             if(recv_ret < 0)
 88             {
 89                 perror("recv");
 90                 return -1;
 91             }
 92             else if(recv_ret == 0)
 93             {
 94                 printf("client close");
 95 
 96                 return -2;
 97             }
 98             data->assign(buf,strlen(buf));
 99             return recv_ret;
100         }                                                                                            
101 
102         //发送
103         ssize_t Send(const string& data)
104         {
105             size_t send_ret = send(sockfd_,data.c_str(),data.size(),0);
106             if(send_ret < 0)
107             {
108                 perror("send");
109                 return -1;
110             }
111             return send_ret;
112         }
113 
114         //关闭套接字
115         void Close()
116         {
117             close(sockfd_);
118 
119             sockfd_ = -1;
120         }
121                                                                                                      
122     private:
123         int sockfd_;
124 };

客户端:

  1 #include<stdio.h>                                                                                                                                                                                                                           
  2 #include<string.h>
  3 #include<unistd.h>
  4 #include<sys/socket.h>
  5 #include<arpa/inet.h>
  6 
  7 int main()
  8 {
  9     int sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
 10     if(sockfd < 0)
 11     {
 12         perror("socket");
 13         return 0;
 14     }
 15 
 16     struct sockaddr_in addr;
 17     addr.sin_family = AF_INET;
 18     addr.sin_port = htons(18989);
 19     addr.sin_addr.s_addr = inet_addr("0.0.0.0");
 20     int ret = connect(sockfd,(struct sockaddr*)&addr,sizeof(addr));
 21     if(ret < 0)
 22     {
 23         perror("connect");
 24         return 0;
 25     }
 26 
 27 
 28     while(1)
 29     {
 30 
 31         sleep(1);
 32 
 33        char buf[1024] = {0};
 34        sprintf(buf,"hello server,i am client2");
 35        ssize_t send_ret = send(sockfd,buf,strlen(buf),0);
 36        if(send_ret < 0)
 37        {
 38            perror("send");
 39            continue;
 40        }
 41 
 42 
 43        memset(buf,'\0',sizeof(buf));
 44        ssize_t recv_ret = recv(sockfd,buf,sizeof(buf)-1,0);
 45        if(recv_ret < 0)
 46        {
 47            perror("recv");
 48            continue;
 49        }
 50        else if(recv_ret == 0)
 51        {
 52            printf("server close");
 53 
 54            close(sockfd);
 55 
 56            return 0;
 57        }
 58 
 59        printf("server say: %s\n",buf);
 60     }
 61 
 62     close(sockfd);
 63     return 0;
 64 }                                                                                       

服务端:

  1 #include"Tcp_thread_server.hpp"                                                                                                                                     
  2 #include<pthread.h>
  3 
  4 void* MyThreadStart(void* arg)
  5 {
  6     TcpSocket* ts = (TcpSocket*)arg;
  7 
  8     while(1)
  9     {
 10         string data = "";
 11         int recv_ret = ts->Recv(&data);
 12         if(recv_ret < 0)
 13         {
 14             printf("recv fail");
 15             continue;
 16         }
 17         else if(recv_ret == -2)
 18         {
 19             printf("client close\n");
 20             ts->Close();
 21             delete ts;
 22             return 0;
 23         }
 24         printf("client say:%s\n",data.c_str());
 25 
 26         data.clear();
 27         data.assign("hello client,i am server");
 28         ssize_t send_ret = ts->Send(data);
 29         if(send_ret < 0)
 30         {
 31             printf("send fail\n");
 32             continue;
 33         }
 34     }
 35     return NULL;
 36 }
 37 
 38 int main()
 39 {
 40     TcpSocket tcp;
 41 
 42     int ret = tcp.Socket();
 43     if(ret < 0)
 44     {
 45         return -1;
 46     }
 47 
 48     ret = tcp.Bind();
 49     if(ret < 0)
 50     {
 51         return -1;
 52     }
 53 
 54     ret = tcp.Listen();
 55     if(ret < 0)
 56     {
 57         return -1;
 58     }
 59 
 60     while(1)
 61     {
 62         struct sockaddr_in addr;
 63         int new_sock = tcp.Accept(&addr);                                                                                                                           
 64         if(new_sock < 0)
 65         {
 66             continue;
 67         }
 68 
 69         TcpSocket* ts = new TcpSocket();
 70         if(ts == NULL)
 71         {
 72             close(new_sock);
 73             continue;
 74         }
 75 
 76         ts->SetSockfd(new_sock);
 77 
 78         pthread_t tid;
 79         int ret = pthread_create(&tid,NULL,MyThreadStart,(void*)ts);
 80         if(ret < 0)
 81         {
 82             perror("pthread_create");
 83 
 84             ts->Close();
 85             delete ts;
 86             continue;
 87         }
 88 
 89         //线程分离
 90         pthread_detach(tid);
 91     }
 92     return 0;
 93 }                                                                                                                   

运行结果如下:

客户端:
Linux——套接字编程(二):TCP的socket编程(流程,接口),TCP 客户端 / 服务端通信(单进程版本,多进程版本,多线程版本)_第14张图片
服务端:
Linux——套接字编程(二):TCP的socket编程(流程,接口),TCP 客户端 / 服务端通信(单进程版本,多进程版本,多线程版本)_第15张图片

你可能感兴趣的:(linux—网络,TCP套接字编程,TCP的socket编程流程,TCP客户端/服务端,通信实现,Linux)