利用Winsock編程由同步和異步方式,同步方式邏輯清晰,編程專注于應(yīng)用,在搶先式的多任務(wù)操作系統(tǒng)中(WinNt、Win2K)采用多線程方式效率基本達(dá)到異步方式的水平,應(yīng)此以下為同步方式編程要點(diǎn)。
1、快速通信
Winsock的Nagle算法將降低小數(shù)據(jù)報(bào)的發(fā)送速度,而系統(tǒng)默認(rèn)是使用Nagle算法,使用
例子:
2、SOCKET的SegMentSize和收發(fā)緩沖
TCPSegMentSize是發(fā)送接受時(shí)單個(gè)數(shù)據(jù)報(bào)的最大長(zhǎng)度,系統(tǒng)默認(rèn)為1460,收發(fā)緩沖大小為8192。
在SOCK_STREAM方式下,如果單次發(fā)送數(shù)據(jù)超過(guò)1460,系統(tǒng)將分成多個(gè)數(shù)據(jù)報(bào)傳送,在對(duì)方接受到的將是一個(gè)數(shù)據(jù)流,應(yīng)用程序需要增加斷幀的判斷。當(dāng)然可以采用修改注冊(cè)表的方式改變1460的大小,但MicrcoSoft認(rèn)為1460是最佳效率的參數(shù),不建議修改。
在工控系統(tǒng)中,建議關(guān)閉Nagle算法,每次發(fā)送數(shù)據(jù)小于1460個(gè)字節(jié)(推薦1400),這樣每次發(fā)送的是一個(gè)完整的數(shù)據(jù)報(bào),減少對(duì)方對(duì)數(shù)據(jù)流的斷幀處理。
3、同步方式中減少斷網(wǎng)時(shí)connect函數(shù)的阻塞時(shí)間
同步方式中的斷網(wǎng)時(shí)connect的阻塞時(shí)間為20秒左右,可采用gethostbyaddr事先判斷到服務(wù)主機(jī)的路徑是否是通的,或者先ping一下對(duì)方主機(jī)的IP地址。
A、采用gethostbyaddr阻塞時(shí)間不管成功與否為4秒左右。
例子:
B、采用PING方式時(shí)間約2秒左右
暫略
4、同步方式中解決recv,send阻塞問(wèn)題
采用select函數(shù)解決,在收發(fā)前先檢查讀寫(xiě)可用狀態(tài)。
A、讀
例子:
B、寫(xiě)
5、改變TCP收發(fā)緩沖區(qū)大小
系統(tǒng)默認(rèn)為8192,利用如下方式可改變。
6、服務(wù)方同一端口多IP地址的bind和listen
在可靠性要求高的應(yīng)用中,要求使用雙網(wǎng)和多網(wǎng)絡(luò)通道,再服務(wù)方很容易實(shí)現(xiàn),用如下方式可建立客戶對(duì)本機(jī)所有IP地址在端口3024下的請(qǐng)求服務(wù)。
在客戶方要復(fù)雜一些,連接斷后,重聯(lián)不成功則應(yīng)換下一個(gè)IP地址連接。也可采用同時(shí)連接好后備用的方式。
7、用TCP/IP Winsock實(shí)現(xiàn)變種Client/Server
傳統(tǒng)的Client/Server為客戶問(wèn)、服務(wù)答,收發(fā)是成對(duì)出現(xiàn)的。而變種的Client/Server是指在連接時(shí)有客戶和服務(wù)之分,建立好通信連接后,不再有嚴(yán)格的客戶和服務(wù)之分,任何方都可主動(dòng)發(fā)送,需要或不需要回答看應(yīng)用而言,這種方式在工控行業(yè)很有用,比如RTDB作為I/O Server的客戶,但I(xiàn)/O Server也可主動(dòng)向RTDB發(fā)送開(kāi)關(guān)狀態(tài)變位、隨即事件等信息。在很大程度上減少了網(wǎng)絡(luò)通信負(fù)荷、提高了效率。
采用1-6的TCP/IP編程要點(diǎn),在Client和Server方均已接收優(yōu)先,適當(dāng)控制時(shí)序就能實(shí)現(xiàn)。
1、快速通信
Winsock的Nagle算法將降低小數(shù)據(jù)報(bào)的發(fā)送速度,而系統(tǒng)默認(rèn)是使用Nagle算法,使用
| int setsockopt( SOCKET s, int level, int optname, const char FAR *optval, int optlen );函數(shù)關(guān)閉它 |
例子:
| SOCKET sConnect; sConnect=::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); int bNodelay = 1; int err; err = setsockopt( sConnect, IPPROTO_TCP, TCP_NODELAY, (char *)&bNodelay, sizoeof(bNodelay));//不采用延時(shí)算法 if (err != NO_ERROR) TRACE ("setsockopt failed for some reason\n");; |
2、SOCKET的SegMentSize和收發(fā)緩沖
TCPSegMentSize是發(fā)送接受時(shí)單個(gè)數(shù)據(jù)報(bào)的最大長(zhǎng)度,系統(tǒng)默認(rèn)為1460,收發(fā)緩沖大小為8192。
在SOCK_STREAM方式下,如果單次發(fā)送數(shù)據(jù)超過(guò)1460,系統(tǒng)將分成多個(gè)數(shù)據(jù)報(bào)傳送,在對(duì)方接受到的將是一個(gè)數(shù)據(jù)流,應(yīng)用程序需要增加斷幀的判斷。當(dāng)然可以采用修改注冊(cè)表的方式改變1460的大小,但MicrcoSoft認(rèn)為1460是最佳效率的參數(shù),不建議修改。
在工控系統(tǒng)中,建議關(guān)閉Nagle算法,每次發(fā)送數(shù)據(jù)小于1460個(gè)字節(jié)(推薦1400),這樣每次發(fā)送的是一個(gè)完整的數(shù)據(jù)報(bào),減少對(duì)方對(duì)數(shù)據(jù)流的斷幀處理。
3、同步方式中減少斷網(wǎng)時(shí)connect函數(shù)的阻塞時(shí)間
同步方式中的斷網(wǎng)時(shí)connect的阻塞時(shí)間為20秒左右,可采用gethostbyaddr事先判斷到服務(wù)主機(jī)的路徑是否是通的,或者先ping一下對(duì)方主機(jī)的IP地址。
A、采用gethostbyaddr阻塞時(shí)間不管成功與否為4秒左右。
例子:
| LONG lPort=3024; struct sockaddr_in ServerHostAddr;//服務(wù)主機(jī)地址 ServerHostAddr.sin_family=AF_INET; ServerHostAddr.sin_port=::htons(u_short(lPort)); ServerHostAddr.sin_addr.s_addr=::inet_addr("192.168.1.3"); HOSTENT* pResult=gethostbyaddr((const char *) & (ServerHostAddr.sin_addr.s_addr),4,AF_INET); if(NULL==pResult) { int nErrorCode=WSAGetLastError(); TRACE("gethostbyaddr errorcode=%d",nErrorCode); } else { TRACE("gethostbyaddr %s\n",pResult->h_name);; } |
B、采用PING方式時(shí)間約2秒左右
暫略
4、同步方式中解決recv,send阻塞問(wèn)題
采用select函數(shù)解決,在收發(fā)前先檢查讀寫(xiě)可用狀態(tài)。
A、讀
例子:
| TIMEVAL tv01 = {0, 1};//1ms鐘延遲,實(shí)際為0-10毫秒 int nSelectRet; int nErrorCode; FD_SET fdr = {1, sConnect}; nSelectRet=::select(0, &fdr, NULL, NULL, &tv01);//檢查可讀狀態(tài) if(SOCKET_ERROR==nSelectRet) { nErrorCode=WSAGetLastError(); TRACE("select read status errorcode=%d",nErrorCode); ::closesocket(sConnect); goto 重新連接(客戶方),或服務(wù)線程退出(服務(wù)方); } if(nSelectRet==0)//超時(shí)發(fā)生,無(wú)可讀數(shù)據(jù) { 繼續(xù)查讀狀態(tài)或向?qū)Ψ街鲃?dòng)發(fā)送 } else { 讀數(shù)據(jù) } |
B、寫(xiě)
| TIMEVAL tv01 = {0, 1};//1ms鐘延遲,實(shí)際為9-10毫秒 int nSelectRet; int nErrorCode; FD_SET fdw = {1, sConnect}; nSelectRet=::select(0, NULL, NULL,&fdw, &tv01);//檢查可寫(xiě)狀態(tài) if(SOCKET_ERROR==nSelectRet) { nErrorCode=WSAGetLastError(); TRACE("select write status errorcode=%d",nErrorCode); ::closesocket(sConnect); //goto 重新連接(客戶方),或服務(wù)線程退出(服務(wù)方); } if(nSelectRet==0)//超時(shí)發(fā)生,緩沖滿或網(wǎng)絡(luò)忙 { //繼續(xù)查寫(xiě)狀態(tài)或查讀狀態(tài) } else { //發(fā)送 } |
5、改變TCP收發(fā)緩沖區(qū)大小
系統(tǒng)默認(rèn)為8192,利用如下方式可改變。
| SOCKET sConnect; sConnect=::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); int nrcvbuf=1024*20; int err=setsockopt( sConnect, SOL_SOCKET, SO_SNDBUF,//寫(xiě)緩沖,讀緩沖為SO_RCVBUF (char *)&nrcvbuf, sizeof(nrcvbuf)); if (err != NO_ERROR) { TRACE("setsockopt Error!\n"); } 在設(shè)置緩沖時(shí),檢查是否真正設(shè)置成功用 int getsockopt( SOCKET s, int level, int optname, char FAR *optval, int FAR *optlen ); |
6、服務(wù)方同一端口多IP地址的bind和listen
在可靠性要求高的應(yīng)用中,要求使用雙網(wǎng)和多網(wǎng)絡(luò)通道,再服務(wù)方很容易實(shí)現(xiàn),用如下方式可建立客戶對(duì)本機(jī)所有IP地址在端口3024下的請(qǐng)求服務(wù)。
| SOCKET hServerSocket_DS=INVALID_SOCKET; struct sockaddr_in HostAddr_DS;//服務(wù)器主機(jī)地址 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("建立數(shù)據(jù)服務(wù)器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 錯(cuò)誤!"); return FALSE; } if(SOCKET_ERROR==::listen(hServerSocket_DS,10))//10個(gè)客戶 { AfxMessageBox("Socket listen 錯(cuò)誤!"); return FALSE; } AfxBeginThread(ServerThreadProc,NULL,THREAD_PRIORITY_NORMAL); |
在客戶方要復(fù)雜一些,連接斷后,重聯(lián)不成功則應(yīng)換下一個(gè)IP地址連接。也可采用同時(shí)連接好后備用的方式。
7、用TCP/IP Winsock實(shí)現(xiàn)變種Client/Server
傳統(tǒng)的Client/Server為客戶問(wèn)、服務(wù)答,收發(fā)是成對(duì)出現(xiàn)的。而變種的Client/Server是指在連接時(shí)有客戶和服務(wù)之分,建立好通信連接后,不再有嚴(yán)格的客戶和服務(wù)之分,任何方都可主動(dòng)發(fā)送,需要或不需要回答看應(yīng)用而言,這種方式在工控行業(yè)很有用,比如RTDB作為I/O Server的客戶,但I(xiàn)/O Server也可主動(dòng)向RTDB發(fā)送開(kāi)關(guān)狀態(tài)變位、隨即事件等信息。在很大程度上減少了網(wǎng)絡(luò)通信負(fù)荷、提高了效率。
采用1-6的TCP/IP編程要點(diǎn),在Client和Server方均已接收優(yōu)先,適當(dāng)控制時(shí)序就能實(shí)現(xiàn)。


