当前位置:   article > 正文

多线程通信---解决TCP通信阻塞问题_tcp阻塞

tcp阻塞

TCP通信阻塞原因:
accept与recv以及send都是阻塞接口,任意一个接口的调用,都有可能会导致服务端流程阻塞
本质原因:当前的服务端,因为不知道什么时候有新连接到来,什么时候那个客户端有数据到来,因此流程只能固定的去调用接口,但是这种调用方式有可能会造成阻塞
解决方案:
多执行流并发处理
为每个客户都创建一个执行流负责与这个客户端进行通信
好处:
1.主线程卡在获取新建连接这里,但是不影响客户端的通信
2. 某个客户端的通信阻塞,也不会影响主线程以及其他线程
在主线程中,获取新建连接,一旦获取到来则创建一个执行流,通过这个新建连接与客户端进行通信
多线程:
普通线程与主线县城数据共享,指定入口函数执行
注意事项:
主线程不能随意释放套接字,因为资源共享,一旦释放其他线程无法使用
tcpsocket.hpp

 #include<cstdio>                                                                                                                                                     
 #include<iostream>
 #include<string>
 #include<unistd.h>
 #include<arpa/inet.h>
 #include<netinet/in.h>
 #include<sys/socket.h>
 #define CHECK_RET(q)  if((q)==false){return -1;}
 #define LISTEN_BACKLOG 5
 class TcpSocket{
    private:
        int _sockfd; //套接字描述符
    public:
        TcpSocket():_sockfd(-1){}
        //1.建立套接字
        bool Socket(){
            //int socket(地址域类型,套接字类型,协议类型)
            _sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
            if(_sockfd<0){
              perror("socket error");
              return false;
            }
            return true;
        }
 
        //2.套接字绑定地址信息
        bool Bind(const std::string &ip,const uint16_t port){
            sockaddr_in addr;  //建立IPv4结构体
            addr.sin_family=AF_INET;  //设置地址域类型为IPv4
            addr.sin_port=htons(port);  //主机字节序端口转换为网络字节序端口
            addr.sin_addr.s_addr=inet_addr(&ip[0]); //将点分十进制转换为网络字节序
            socklen_t len=sizeof(sockaddr_in);
            //int bind(操作句柄,当前地址信息,地址信息长度)
            int ret=bind(_sockfd,(sockaddr*)&addr,len);
            if(ret<0){
              perror("bind error");
             return false;
            }
            return true;
        }
 
        //3.开始监听
        //int listen(描述符,同一时间连接数)
        bool Listen(int backlog=LISTEN_BACKLOG){
            int ret=listen(_sockfd,backlog);
            if(ret<0){
              perror("listen error");
              return false;
            }
            return true;
        }
 
        //4.客户端发送连接请求
        bool Connect(const std::string &ip,const uint16_t port){
            //int connect(描述符,服务端地址,地址长度)
            sockaddr_in addr;  //创建IPv4结构体
            addr.sin_family=AF_INET;  //地址域类型为IPv4
            addr.sin_port=htons(port); //主机字节序转换为网络字节序
            addr.sin_addr.s_addr=inet_addr(&ip[0]); //将点分十进制主机字节序转换为网络字节序
            socklen_t len=sizeof(sockaddr_in);
            int ret=connect(_sockfd,(sockaddr*)&addr,len);
            if(ret<0){
              perror("connect error");
              return false;
            }
            return true;
        }
 
        //5.服务端获取新连接
        bool Accept(TcpSocket *sock,std::string *ip=NULL,uint16_t *port=NULL){
             //int accept(监听套接字,客户端地址,长度)
             sockaddr_in addr;               
             socklen_t len=sizeof(sockaddr_in);
             int newfd=accept(_sockfd,(sockaddr*)&addr,&len);
             if(newfd<0){
               perror("accept error");
               return false;
             }
             sock->_sockfd=newfd;
             if(ip!=NULL){  //新连接成功
               *ip=inet_ntoa(addr.sin_addr);
             } 
             if(port!=NULL){ //新连接成功
               *port=ntohs(addr.sin_port);
             }
             return true;
        }
 
        //收发数据(tcp通信因为socket包含完整五元组因此不需要指定地址)
        bool Recv(std::string *buf){
             //int recv(描述符,空间,数据长度,标志位)
             char tmp[4096]={0};
             int ret=recv(_sockfd,tmp,4096,0);
             if(ret<0){
               perror("recv error");
               return false;
             }else if(ret==0){
               printf("peer shutdown");
               return false;
             }
             buf->assign(tmp,ret);
             return true;
        }
        bool Send(const std::string &data){
            //int send(描述符,数据,长度,标志位)
            int total=0;
            while(total<data.size()){      
              int ret=send(_sockfd,&data[0]+total,data.size()-total,0);
              if(ret<0){
                perror("send error");
                return false;
              }
              total+=ret;
            }
            return true;
        }
 
        //关闭连接
        bool Close(){
        if(_sockfd!=-1){
          close(_sockfd);
        }
        return true;
        }
 };                   

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126

thread_srv.cpp

 #include"tcpsocket.hpp"                                                                                                                                              
 #include<pthread.h>
 //accept和recv以及send都是阻塞接口,任意一个接口的调用,都有可能导致服务器阻塞
 //解决方案:多执行流并发操作
 //1.多线程:普通线程与主线程数据共享,指定入口函数执行
 //2.多进程:子进程复制父进程,但是数据独有
 
 void *thr_entry(void *arg)
 {
     bool ret;
     TcpSocket *clisock=(TcpSocket*)arg;
     while(1){
        //5.收发数据--使用获取的新建套接字进行通信
         std::string buf;
         ret=clisock->Recv(&buf);
         if(ret==false){
            clisock->Close();
            delete clisock;
            return NULL;
         }
         std::cout<<"new client say:"<<buf<<std::endl;
 
         buf.clear();
         std::cout<<"new server say: ";
         std::cin>>buf;
         ret=clisock->Send(buf);
         if(ret==false){
           clisock->Close();
           delete clisock;
           return NULL;
         }
     }
     clisock->Close();
     delete clisock;
     return NULL;
 
}
 
 int main(int argc,char* argv[])
 {
    //通过程序运行参数指定服务端绑定的地址
    // ./tcp_srv 172.17.0.4 9000
    if(argc!=3){
      printf("usage: ./tcp_srv 172.17.0.4",9000);
      return -1;
    }
    std::string srvip=argv[1];  //服务端IP
    uint16_t srvport=std::stoi(argv[2]);  //服务端端口
    TcpSocket lst_sock; //监听套接字
    //1.创建套接字
    CHECK_RET(lst_sock.Socket());
    //2.绑定地址信息
    CHECK_RET(lst_sock.Bind(srvip,srvport));
    //3.开始监听
    CHECK_RET(lst_sock.Listen());
    while(1){
       //4.获取新建链接
       TcpSocket *clisock=new TcpSocket();
       std::string cliip;
       uint16_t cliport;
       bool ret=lst_sock.Accept(clisock,&cliip,&cliport);
       if(ret==false){
          continue;
       }
       std::cout<<"get newconnect:"<<cliip<<"-"<<cliport<<"\n";
       //创建线程专门负责与指定客户端的通信
      pthread_t tid;
       pthread_create(&tid,NULL,thr_entry,(void*)clisock);
       pthread_detach(tid);
    }
    //6.关闭套接字
    lst_sock.Close();
    return 0;
 }       
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74

tcp_cli.cpp

1 #include"tcpsocket.hpp"
  2 
  3 int main(int argc,char *argv[])
  4 {
  5     //通过参数传入要连接的服务器的地址信息
  6     if(argc!=3){
  7       printf("usage:./tcp_cli srvip srvport\n");
  8       return -1;
  9     }
 10     std::string srvip=argv[1];
 11     uint16_t  srvport=std::stoi(argv[2]); //将字符串转换为十进制
 12 
 13     TcpSocket cli_sock;
 14     //1.创建套接字
 15     CHECK_RET(cli_sock.Socket());
 16     //2.绑定地址信息(不推荐)_
 17     //3.向服务端发起连接
 18     CHECK_RET(cli_sock.Connect(srvip,srvport));
 19     while(1){
 20       //4.接收数据
 21         std::string buf;
 22         std::cout<<"client say:";                                                                                                                                    
 23         std::cin>>buf;
 24         CHECK_RET(cli_sock.Send(buf));
 25 
 26         buf.clear();
 27         CHECK_RET(cli_sock.Recv(&buf));
 28         std::cout<<"server say:"<<buf<<std::endl;
 29     }
 30     //5.关闭套接字
 31     CHECK_RET(cli_sock.Close());
 32     return 0;
 33 }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/594536
推荐阅读
相关标签
  

闽ICP备14008679号