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,&iMode);

ioctlsocket,WSAAsyncselect()和WSAEventselect()

 
 
static int tcp_send(SOCKET sockClient,const char* request_head,long slen,bool &iFlag)
{
	
	int iRet = 0;
	int i = 0;
       long already_bytes = 0;
       int lenSend = 0;

	fd_set fd;
	timeval tiout;
	tiout.tv_sec = 1;
	tiout.tv_usec = 0;

	for (i = 0;(i < 3) && (!iFlag); i++)//iFlag 即bexit时退出,防止在此循环中等待事件过长
	{
		FD_ZERO(&fd);
		FD_SET(sockClient, &fd);
		iRet = select(sockClient+1,NULL,&fd,NULL,&tiout);

		if (iRet > 0  && FD_ISSET(sockClient,&fd))
		{ 
		    lenSend = send(sockClient, request_head+already_bytes, slen-already_bytes, 0);
            if(lenSend == -1)
            {
                continue;	    
            }
           already_bytes += lenSend;
           if(already_bytes == slen)
           {
               return 0;
           }
        }
		else // 无论出错超时还是暂时没有等到,都尝试3次
		{
			continue;
		}
		
	}

	return -1;
}

static int tcp_connect(SOCKET sockClient,char* remote_host,int remote_port ,bool &iFlag)
{	 
	
	int iRet = 0;
	int i = 0;
	string  remote_ip = remote_host;
	struct sockaddr_in	remote_addr;
	remote_addr.sin_family = AF_INET;
	remote_addr.sin_addr.S_un.S_addr = inet_addr(remote_ip.c_str());
	remote_addr.sin_port = htons(remote_port);	 

	
	  // 尝试去连接服务端  
	  iRet = connect(sockClient,(struct sockaddr *)&remote_addr,sizeof(SOCKADDR));
	  if (0 == iRet)  
	  {  
		  return 0 ; // 连接成功  
	  }  
	  else if (iRet < 0 && GetLastError() == 10035) //errno == EINPROGRESS表示正在建立链接
	  {  
		  fd_set fd;
		  timeval tiout;
		  tiout.tv_sec = 1;
		  tiout.tv_usec = 0;
          for (i = 0; (i < 3) && (!iFlag); i++)//iFlag 即bexit时退出,防止在此循环中等待时间过长
          {
              FD_ZERO(&fd);  
              FD_SET(sockClient, &fd); //相反的是FD_CLR(sockClient, &fd) 

              iRet = select(sockClient+1,NULL,&fd,NULL,&tiout);
              if (iRet <= 0)  
              {  
                  continue; // 有错误(select错误或者超时)  
              }  
              //将检测到sockClient读事件或写时间,并不能说明connect成功
              if(FD_ISSET(sockClient,&fd))
              {   
                  int error = -1;  
                  int optLen = sizeof(int);  

                  if(getsockopt(sockClient, SOL_SOCKET, SO_ERROR, (char*)&error, &optLen) < 0)
                  {
                      continue; //建立失败close(sockClient)
                  }
                  if (0 != error)  
                  {  
                      continue; // 有错误  
                  }  
                  else  
                  {
                      return 0;  // 无错误  
                  }  
              }		 
	      }  
       }
	  else
	  {
		 return -1;;//出现错误 
	  }
	return -1;
}

static int tcp_receive(SOCKET sockClient, char *pOut,int len ,bool &iFlag)
{
	int iRet = 0;
	int i = 0;
	
	fd_set fd;
	timeval tiout;
	tiout.tv_sec = 1;
	tiout.tv_usec = 0;

	for (i = 0; (i < 3) && (!iFlag); i++)//iFlag 即bexit时退出,防止在此循环中等待事件过长
	{
		FD_ZERO(&fd);
		FD_SET(sockClient, &fd);
		iRet = select(sockClient+1,&fd,NULL,NULL,&tiout);
		
		if (iRet > 0  && FD_ISSET(sockClient,&fd))
		{
			return recv(sockClient, pOut, len, 0);
		}
		else // 无论出错超时还是暂时没有等到,都尝试3次
		{
			continue;
		}
		
	}
	
	return -1;
}

猜你喜欢

转载自blog.csdn.net/alisa_xf/article/details/80183045