非阻塞套接字用法
阿新 • • 發佈:2019-02-16
http://www.cppblog.com/yunboy4/archive/2010/03/16/91231.html
1.非阻塞套接字的模式 (1)伺服器端 通常socket執行後預設為阻塞模式。要呼叫ioctlsocket函式設定非阻塞模式。 如: WSAData Data; WSAStartup(MAKEWORD(2, 2), &Data); SerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(INVALID_SOCKET == SerSocket) cout<<"Invalid Socket!\n"; u_long iMode = 1; ioctlsocket(SerSocket, FIONBIO, &iMode); 在接受客戶端請求的執行緒中,若接受成功就返回客戶端的套接字,否則返回INVALID_SOCKET錯誤, 若錯誤程式碼為WSAEWOULDBLOCK,說明當前沒有客戶端請求。 如: //接受客戶端請求執行緒 DWORD WINAPI AcceptClientPro(LPVOID LpP) { SOCKADDR_IN ClientAdrr; int AddrLen = sizeof(SOCKADDR); //非阻塞模式 while (!IsConnet) { ClientSock = accept(SerSocket, (SOCKADDR *)&ClientAdrr, &AddrLen ); if(INVALID_SOCKET == ClientSock) { int n = WSAGetLastError(); //沒有客戶端請求 if(WSAEWOULDBLOCK == n) { cout<<"沒有客戶端發出請求!"<<endl; Sleep(1000); continue; }else { cout<<"出現錯誤!"<<endl; Sleep(1000); } }else { cout<<"已連線客戶端!"<<endl; IsConnet = true; break; } } return 0; } 就recv函式來說,在阻塞模式中,如果沒有客戶端傳送資料過來,執行緒到這裡會阻塞,直到有數 據傳送過來為止。在非阻塞模式中,沒有客戶端傳送資料過來,返回SOCKER_ERROR,錯誤程式碼為WSAEWOULDBLOCK。 如: //接收資料執行緒 DWORD WINAPI ReceiveDataPro(LPVOID LpP) { while(!IsConnet); //保證連線後再接受資料 while(1) { if(IsReadyRecei) //保證緩衝區在未處理時不受新來的資料的影響 { int ReceiLen = recv(ClientSock, (char *)&DataPack, sizeof(DataPack), 0); if(SOCKET_ERROR == ReceiLen) { int Err = WSAGetLastError(); if(WSAEWOULDBLOCK == Err) { cout<<"沒有收到資料"<<endl; continue; } else if (WSAENETDOWN == Err ||//客戶端關閉了連線 WSAETIMEDOUT == Err || WSAECONNRESET == Err ) { cout<<"伺服器關閉了連線"<<endl; break; } } if(0 == ReceiLen) //客戶端關閉了連線 { cout<<"ReceiLen = 0"<<endl; break; } if(ReceiLen >= sizeof(DataPack)) //成功接收 { cout<<"已收到資料:"<<DataPack.buf<<endl; IsReadyRecei = false; break; } } } return 0; } (2)客戶端 在客戶端的連線請求執行緒中,connect函式會返回SOCKET_ERROR,這並不是說明連線失敗,具體情況 要看它的WSAGetLastError()返回值,若它三次返回SOCKET_ERROR的Error程式碼依次為WSAEWOULDBLOCK, WSAEINVAL,WSAEISCONN,就說明連線伺服器成功,否則失敗。但有的時候WSAEINVAL沒有出現就有WSAEISCONN 了,所以我還是以WSAEISCONN為連線完成的標誌,但要注意其實在三次返回程式碼中,第一次的WSAEWOULDBLOCK 之前的connect操作就成功了,如果沒出意外伺服器就要響應了。 如: //連線伺服器執行緒 DWORD WINAPI ConnetServerPro(LPVOID LpP) { SOCKADDR_IN ServerAddr; ServerAddr.sin_family = AF_INET; ServerAddr.sin_port = htons(1200); ServerAddr.sin_addr.s_addr = inet_addr("192.168.1.100"); int BlockFlag = 0; int InvalFlag = 0; while (!IsConnet) { int nResu = connect(ClientSock, (SOCKADDR *)&ServerAddr, sizeof(SOCKADDR)); if(SOCKET_ERROR == nResu) { int n = WSAGetLastError(); if(WSAEWOULDBLOCK == n ) //不能立即完成 { cout<<"過程1!"<<endl; BlockFlag++; continue; } else if(WSAEINVAL == n) //監聽狀態 { cout<<"過程2!"<<endl; InvalFlag++; continue; } else if(WSAEISCONN == n) //連線完成 { cout<<"已連線伺服器!"<<endl; IsConnet = true; break; } else { cout<<"出現其他錯誤!\n"<<endl; Sleep(1000); } } } return 0; } 在傳送資料執行緒中,send()返回的是傳送資料的長度說明發送成功;返回SOCKET_ERROR時,若 錯誤程式碼為WSAEWOULDBLOCK就再重試,不是WSAEWOULDBLOCK就說明有錯誤。】 如: //傳送資料執行緒 DWORD WINAPI SendDataPro(LPVOID LpP) { while(!IsConnet); //保證已連線伺服器 while(1) { int len = send(ClientSock, (char *)&DataPack, DataPack.Head.len, 0); if(SOCKET_ERROR == len) { int Error = WSAGetLastError(); if(WSAEWOULDBLOCK == Error) continue; } else //傳送成功 { cout<<"傳送成功!"<<endl; break; } } return 0; }