TCP非阻塞设置

 

套接字的默认状态是阻塞的,这就意味着当发出一个不能立即完成的套接字调用时,其进程将被投入睡眠,等待响应操作完成,可能阻塞的套接字调用可分为以下四类:

(1) 输入操作,包括read,readv,recv,recvfrom,recvmsg;

(2) 输出操作,包括write,writev,send,sendto,sendmsg;

(3) 接受外来连接,即accept函数。

accept在阻塞模式下,没有新连接时,线程会进入睡眠状态;非阻塞模式下,没有新连接时,立即返回WOULDBLOCK错误。

(4) 发起外出连接,即tcp的connect函数;

connect在阻塞模式下,仅TCP连接建立成功或出错时才返回,分几种具体的情况,这里不再叙述;非阻塞模式下,该函数会立即返回INPROCESS错误(需用select检测该连接是否建立成功

linux下和windows下设置阻塞和非阻塞的几种方式:

linux:

fcntl(socket, F_SETFL, flags | O_NONBLOCK);

windows:

ioctlsocket(socket,FIONBIO,1);//FIONBIO :允许或禁止套接口s的非阻塞模式 ;  0是堵塞

ioctlsocket,WSAAsyncselect()和WSAEventselect()


代码:

  1. static int tcp_send(SOCKET sockClient,const char* request_head,long slen,bool &iFlag)  
  2. {  
  3.       
  4.     int iRet = 0;  
  5.     int i = 0;  
  6.     long already_bytes = 0;  
  7.     int lenSend = 0;  
  8.   
  9.     fd_set fd;  
  10.     timeval tiout;  
  11.     tiout.tv_sec = 1;  
  12.     tiout.tv_usec = 0;  
  13.   
  14.     for (i = 0;(i < 3) && (!iFlag); i++)//iFlag 即bexit时退出,防止在此循环中等待事件过长  
  15.     {  
  16.         FD_ZERO(&fd);  
  17.         FD_SET(sockClient, &fd);  
  18.         iRet = select(sockClient+1,NULL,&fd,NULL,&tiout);  
  19.   
  20.         if (iRet > 0  && FD_ISSET(sockClient,&fd))  
  21.         {   
  22.             lenSend = send(sockClient, request_head+already_bytes, slen-already_bytes, 0);  
  23.             if(lenSend == -1)  
  24.             {  
  25.                 continue;         
  26.             }  
  27.            already_bytes += lenSend;  
  28.            if(already_bytes == slen)  
  29.            {  
  30.                return 0;  
  31.            }  
  32.         }  
  33.         else // 无论出错超时还是暂时没有等到,都尝试3次  
  34.         {  
  35.             continue;  
  36.         }  
  37.           
  38.     }  
  39.   
  40.     return -1;  
  41. }  
  42.   
  43. static int tcp_connect(SOCKET sockClient,char* remote_host,int remote_port ,bool &iFlag)  
  44. {      
  45.       
  46.     int iRet = 0;  
  47.     int i = 0;  
  48.     string  remote_ip = remote_host;  
  49.     struct sockaddr_in  remote_addr;  
  50.     remote_addr.sin_family = AF_INET;  
  51.     remote_addr.sin_addr.S_un.S_addr = inet_addr(remote_ip.c_str());  
  52.     remote_addr.sin_port = htons(remote_port);     
  53.   
  54.       
  55.       // 尝试去连接服务端    
  56.       iRet = connect(sockClient,(struct sockaddr *)&remote_addr,sizeof(SOCKADDR));  
  57.       if (0 == iRet)    
  58.       {    
  59.           return 0 ; // 连接成功    
  60.       }    
  61.       else if (iRet < 0 && GetLastError() == 10035) //errno == EINPROGRESS表示正在建立链接  
  62.       {    
  63.           fd_set fd;  
  64.           timeval tiout;  
  65.           tiout.tv_sec = 1;  
  66.           tiout.tv_usec = 0;  
  67.           for (i = 0; (i < 3) && (!iFlag); i++)//iFlag 即bexit时退出,防止在此循环中等待时间过长  
  68.           {  
  69.               FD_ZERO(&fd);    
  70.               FD_SET(sockClient, &fd); //相反的是FD_CLR(sockClient, &fd)   
  71.   
  72.               iRet = select(sockClient+1,NULL,&fd,NULL,&tiout);  
  73.               if (iRet <= 0)    
  74.               {    
  75.                   continue// 有错误(select错误或者超时)    
  76.               }    
  77.               //将检测到sockClient读事件或写时间,并不能说明connect成功  
  78.               if(FD_ISSET(sockClient,&fd))  
  79.               {     
  80.                   int error = -1;    
  81.                   int optLen = sizeof(int);    
  82.   
  83.                   if(getsockopt(sockClient, SOL_SOCKET, SO_ERROR, (char*)&error, &optLen) < 0)  
  84.                   {  
  85.                       continue//建立失败close(sockClient)  
  86.                   }  
  87.                   if (0 != error)    
  88.                   {    
  89.                       continue// 有错误    
  90.                   }    
  91.                   else    
  92.                   {  
  93.                       return 0;  // 无错误    
  94.                   }    
  95.               }        
  96.           }    
  97.        }  
  98.       else  
  99.       {  
  100.          return -1;;//出现错误   
  101.       }  
  102.     return -1;  
  103. }  
  104.   
  105. static int tcp_receive(SOCKET sockClient, char *pOut,int len ,bool &iFlag)  
  106. {  
  107.     int iRet = 0;  
  108.     int i = 0;  
  109.       
  110.     fd_set fd;  
  111.     timeval tiout;  
  112.     tiout.tv_sec = 1;  
  113.     tiout.tv_usec = 0;  
  114.   
  115.     for (i = 0; (i < 3) && (!iFlag); i++)//iFlag 即bexit时退出,防止在此循环中等待事件过长  
  116.     {  
  117.         FD_ZERO(&fd);  
  118.         FD_SET(sockClient, &fd);  
  119.         iRet = select(sockClient+1,&fd,NULL,NULL,&tiout);  
  120.           
  121.         if (iRet > 0  && FD_ISSET(sockClient,&fd))  
  122.         {  
  123.             return recv(sockClient, pOut, len, 0);  
  124.         }  
  125.         else // 无论出错超时还是暂时没有等到,都尝试3次  
  126.         {  
  127.             continue;  
  128.         }  
  129.           
  130.     }  
  131.       
  132.     return -1;  
  133. }  

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
阻塞式接收函数是指在没有数据到达时,该函数不会一直等待数据到达,而是立即返回,以便程序能够执行其他任务。在TCP编程中,常用的阻塞式接收函数是recv()函数。 使用阻塞式接收函数需要先将socket设置阻塞模式,可以通过fcntl或者ioctl函数设置。然后,在调用recv()函数时,如果没有数据到达,该函数会立即返回,并且返回值为-1,同时设置errno为EAGAIN或EWOULDBLOCK。如果有数据到达,recv()函数会返回接收到的字节数。 下面是一个使用阻塞式接收函数的示例代码: ```c #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <stdio.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> int main() { int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 将socket设置阻塞模式 int flags = fcntl(sockfd, F_GETFL, 0); fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); struct sockaddr_in servaddr; memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(8888); servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); char buf[1024]; int n = 0; while (1) { n = recv(sockfd, buf, sizeof(buf), 0); if (n == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { // 没有数据到达,可以执行其他任务 // ... } else { // 接收错误,关闭socket并退出循环 close(sockfd); break; } } else if (n == 0) { // 对方关闭连接,关闭socket并退出循环 close(sockfd); break; } else { // 接收到数据,处理数据 // ... } } return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值