套接字的默认状态是阻塞的,这就意味着当发出一个不能立即完成的套接字调用时,其进程将被投入睡眠,等待响应操作完成,可能阻塞的套接字调用可分为以下四类:
(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()
代码:
- 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;
- }