套接字的默认状态是阻塞的,这就意味着当发出一个不能立即完成的套接字调用时,其进程将被投入睡眠,等待响应操作完成,可能阻塞的套接字调用可分为以下四类:
(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:
扫描二维码关注公众号,回复:
129806 查看本文章
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; }