采用select函数解决,在收发前先检查读写可用状态。
A、读
例子:
| TIMEVAL tv01 = {0, 1};//1ms钟延迟,实际为0-10毫秒 int nSelectRet; int nErrorCode; FD_SET fdr = {1, sConnect}; nSelectRet=::select(0, &fdr, NULL, NULL, &tv01);//检查可读状态 if(SOCKET_ERROR==nSelectRet) { nErrorCode=WSAGetLastError(); TRACE("select read status errorcode=%d",nErrorCode); ::closesocket(sConnect); goto 重新连接(客户方),或服务线程退出(服务方); } if(nSelectRet==0)//超时发生,无可读数据 { 继续查读状态或向对方主动发送 } else { 读数据 } |
B、写
| TIMEVAL tv01 = {0, 1};//1ms钟延迟,实际为9-10毫秒 int nSelectRet; int nErrorCode; FD_SET fdw = {1, sConnect}; nSelectRet=::select(0, NULL, NULL,&fdw, &tv01);//检查可写状态 if(SOCKET_ERROR==nSelectRet) { nErrorCode=WSAGetLastError(); TRACE("select write status errorcode=%d",nErrorCode); ::closesocket(sConnect); //goto 重新连接(客户方),或服务线程退出(服务方); } if(nSelectRet==0)//超时发生,缓冲满或网络忙 { //继续查写状态或查读状态 } else { //发送 } |
5、改变TCP收发缓冲区大小
系统默认为8192,利用如下方式可改变。
| SOCKET sConnect; sConnect=::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); int nrcvbuf=1024*20; sConnect, SOL_SOCKET, SO_SNDBUF,//写缓冲,读缓冲为SO_RCVBUF (char *)&nrcvbuf, sizeof(nrcvbuf)); if (err != NO_ERROR) { TRACE("setsockopt Error!\n"); } 在设置缓冲时,检查是否真正设置成功用 int getsockopt( SOCKET s, int level, int optname, char FAR *optval, int FAR *optlen ); |
6、服务方同一端口多IP地址的bind和listen
在可靠性要求高的应用中,要求使用双网和多网络通道,再服务方很容易实现,用如下方式可建立客户对本机所有IP地址在端口3024下的请求服务。
| SOCKET hServerSocket_DS=INVALID_SOCKET; struct sockaddr_in HostAddr_DS;//服务器主机地址 LONG lPort=3024; HostAddr_DS.sin_family=AF_INET; HostAddr_DS.sin_port=::htons(u_short(lPort)); HostAddr_DS.sin_addr.s_addr=htonl(INADDR_ANY); hServerSocket_DS=::socket( AF_INET, SOCK_STREAM,IPPROTO_TCP); if(hServerSocket_DS==INVALID_SOCKET) { AfxMessageBox("建立数据服务器SOCKET 失败!"); return FALSE; } if(SOCKET_ERROR==::bind(hServerSocket_DS,(struct sockaddr *)(&(HostAddr_DS)),sizeof(SOCKADDR))) { int nErrorCode=WSAGetLastError (); TRACE("bind error=%d\n",nErrorCode); AfxMessageBox("Socket Bind 错误!"); return FALSE; } if(SOCKET_ERROR==::listen(hServerSocket_DS,10))//10个客户 { AfxMessageBox("Socket listen 错误!"); return FALSE; } AfxBeginThread(ServerThreadProc,NULL,THREAD_PRIORITY_NORMAL); |
在客户方要复杂一些,连接断后,重联不成功则应换下一个IP地址连接。也可采用同时连接好后备用的方式。
7、用TCP/IP Winsock实现变种Client/Server
传统的Client/Server为客户问、服务答,收发是成对出现的。而变种的Client/Server是指在连接时有客户和服务之分,建立好通信连接后,不再有严格的客户和服务之分,任何方都可主动发送,需要或不需要回答看应用而言,这种方式在工控行业很有用,比如RTDB作为I/O Server的客户,但I/O Server也可主动向RTDB发送开关状态变位、随即事件等信息。在很大程度上减少了网络通信负荷、提高了效率。
采用1-6的TCP/IP编程(www.cppentry.com)要点,在Client和Server方均已接收优先,适当控制时序就能实现。