From: http://blog.csdn.net/woshinia/article/details/8585930
部分代碼參考《[WINDOWS網路與通訊程式設計].王豔平》,網路中一些I/O模型的代碼都沒有對socket是否可寫做過深入研究,我這邊會提供一些解決方案。
阻塞模式下,send會發生阻塞(非阻塞模式下send返回WSAEWOULDBLOCK錯誤,重疊I/O下表現為投遞的發送請求一直無法完成)的情況一般可以分為3種 :
1, 伺服器雖然發送了大量資料,但用戶端並未調用recv函數去接。
2,網路狀況不佳,發送緩衝區中的資料一直發不出去。
3,發送資料量很大,如下載功能,協議發送資料的速度比不上send函數將資料拷貝到發送緩衝區的速度。
對於1,2情況,我們似乎可以直接關閉通訊端,讓用戶端重新請求。但對於3,卻不行。而且實際操作過程中,我們無法區分是1,2,還是3,我們能做的是盡量去保證發送的正確性。當然防止1情況或者2情況中長時間網路不暢,可以設定逾時。若socket一直處於不可寫狀態超過1分鐘,那麼就關閉通訊端。在最後的IOCP模型中就加入了這種逾時機制。其他模型若要加入,可參考它來做。
一,基本的阻塞模型
[cpp] view plain copy print ? #include <WinSock2.h> #include <Windows.h> #include <stdio.h> #pragma comment(lib,"Ws2_32.lib") DWORD WINAPI WorkThread(void* param) { SOCKET* psClient = (SOCKET*)param; char buf[4096]; while(true) { int len = recv(*psClient,buf,4096,0); if(len <= 0) { printf("recv失敗。%d\n",WSAGetLastError()); Sleep(5000); break; } buf[len] = '\0'; printf("收到資料:%s\n",buf); } closesocket(*psClient); delete psClient; return 0; } int main() { WSAData wsaData; if(0 != WSAStartup(MAKEWORD(2,2),&wsaData)) { printf("WSAStartup失敗。\n",WSAGetLastError()); Sleep(5000); return 0; } USHORT nPort = 3456; SOCKET sListen = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); sockaddr_in sin; sin.sin_family = AF_INET; sin.sin_port = htons(nPort); sin.sin_addr.S_un.S_addr = INADDR_ANY; if(SOCKET_ERROR == ::bind(sListen,(sockaddr*)&sin,sizeof(sin))) { printf("bind失敗。%d\n",WSAGetLastError()); Sleep(5000); return -1; } ::listen(sListen,5); while(true) { sockaddr_in addrRemote; int nAddrLen = sizeof(addrRemote); SOCKET *psClient = new SOCKET; *psClient = accept(sListen,(sockaddr*)&addrRemote,&nAddrLen); HANDLE hThread = CreateThread(NULL,0,WorkThread,psClient,0,NULL); CloseHandle(hThread); } closesocket(sListen); WSACleanup(); }
#include <WinSock2.h>#include <Windows.h>#include <stdio.h>#pragma comment(lib,"Ws2_32.lib")DWORD WINAPI WorkThread(void* param){SOCKET* psClient = (SOCKET*)param;char buf[4096];while(true){int len = recv(*psClient,buf,4096,0);if(len <= 0){printf("recv失敗。%d\n",WSAGetLastError());Sleep(5000);break;}buf[len] = '\0';printf("收到資料:%s\n",buf);}closesocket(*psClient);delete psClient;return 0;}int main(){WSAData wsaData;if(0 != WSAStartup(MAKEWORD(2,2),&wsaData)){printf("WSAStartup失敗。\n",WSAGetLastError());Sleep(5000);return 0;}USHORT nPort = 3456;SOCKET sListen = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(nPort);sin.sin_addr.S_un.S_addr = INADDR_ANY;if(SOCKET_ERROR == ::bind(sListen,(sockaddr*)&sin,sizeof(sin))){printf("bind失敗。%d\n",WSAGetLastError());Sleep(5000);return -1;}::listen(sListen,5);while(true){sockaddr_in addrRemote;int nAddrLen = sizeof(addrRemote);SOCKET *psClient = new SOCKET;*psClient = accept(sListen,(sockaddr*)&addrRemote,&nAddrLen);HANDLE hThread = CreateThread(NULL,0,WorkThread,psClient,0,NULL);CloseHandle(hThread);}closesocket(sListen);WSACleanup();}
二,無任何最佳化的非阻塞模型
[cpp] view plain copy print ? #include <WinSock2.h> #include <Windows.h> #include <stdio.h> #include <vector> using namespace std; #pragma comment(lib,"Ws2_32.lib") CRITICAL_SECTION g_cs; HANDLE g_StartEvent; vector<SOCKET> g_vecClients; int g_iVecSize = 0; DWORD WINAPI WorkThread(void* param) { char buf[4096]; while(1) { if(g_vecClients.empty()) { ResetEvent(g_StartEvent); WaitForSingleObject(g_StartEvent,INFINITE); } EnterCriticalSection(&g_cs); for(vector<SOCKET>::iterator it = g_vecClients.begin();it != g_vecClients.end();) { int len = recv(*it,buf,4096,0); if(len == SOCKET_ERROR) { if(WSAEWOULDBLOCK != WSAGetLastError()) { printf("recv Error:%d\n",WSAGetLastError()); closesocket(*it); it = g_vecClients.erase(it); } else { printf("%d.",*it); ++it; } } else { buf[len] = 0; printf("收到資料: %s\n",buf); ++it; } } LeaveCriticalSection(&g_cs); Sleep(100); } return 0; } int main() { InitializeCriticalSectionAndSpinCount(&g_cs,4000); g_StartEvent = CreateEvent(NULL,FALSE,FALSE,NULL); WSAData wsaDate; WSAStartup(MAKEWORD(2,2),&wsaDate); USHORT nport = 3456; u_long ul = 1; SOCKET s = socket(AF_INET,SOCK_STREAM,0); ioctlsocket(s,FIONBIO,&ul); sockaddr_in sin; sin.sin_family = AF_INET; sin.sin_port = htons(nport); sin.sin_addr.S_un.S_addr = ADDR_ANY; if(SOCKET_ERROR == ::bind(s,(sockaddr*)&sin,sizeof(sin))) { return -1; } ::listen(s,5); HANDLE hThread = CreateThread(NULL,0,WorkThread,NULL,0,NULL); CloseHandle(hThread); while(true) { sockaddr_in addrRemote; int nAddrLen = sizeof(addrRemote); SOCKET sClient = accept(s,(sockaddr*)&addrRemote,&nAddrLen); if(sClient != SOCKET_ERROR) { EnterCriticalSection(&g_cs); g_vecClients.push_back(sClient); LeaveCriticalSection(&g_cs); if(g_vecClients.size() == 1) SetEvent(g_StartEvent); } else if(WSAEWOULDBLOCK == WSAGetLastError()) { printf("."); Sleep(100); } else { printf("accept failed! %d\n",WSAGetLastError()); } } closesocket(s); WSACleanup(); CloseHandle(g_StartEvent); DeleteCriticalSection(&g_cs); }
#include <WinSock2.h>#include <Windows.h>#include <stdio.h>#include <vector>using namespace std;#pragma comment(lib,"Ws2_32.lib")CRITICAL_SECTION g_cs;HANDLE g_StartEvent;vector<SOCKET> g_vecClients;int g_iVecSize = 0;DWORD WINAPI WorkThread(void* param){char buf[4096];while(1){if(g_vecClients.empty()){ResetEvent(g_StartEvent);WaitForSingleObject(g_StartEvent,INFINITE);}EnterCriticalSection(&g_cs);for(vector<SOCKET>::iterator it = g_vecClients.begin();it != g_vecClients.end();){int len = recv(*it,buf,4096,0);if(len == SOCKET_ERROR){if(WSAEWOULDBLOCK != WSAGetLastError()){printf("recv Error:%d\n",WSAGetLastError());closesocket(*it);it = g_vecClients.erase(it);}else{printf("%d.",*it);++it;}}else{buf[len] = 0;printf("收到資料: %s\n",buf);++it;}}LeaveCriticalSection(&g_cs);Sleep(100);}return 0;}int main(){InitializeCriticalSectionAndSpinCount(&g_cs,4000);g_StartEvent = CreateEvent(NULL,FALSE,FALSE,NULL);WSAData wsaDate;WSAStartup(MAKEWORD(2,2),&wsaDate);USHORT nport = 3456;u_long ul = 1;SOCKET s = socket(AF_INET,SOCK_STREAM,0);ioctlsocket(s,FIONBIO,&ul);sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(nport);sin.sin_addr.S_un.S_addr = ADDR_ANY;if(SOCKET_ERROR == ::bind(s,(sockaddr*)&sin,sizeof(sin))){return -1;}::listen(s,5);HANDLE hThread = CreateThread(NULL,0,WorkThread,NULL,0,NULL);CloseHandle(hThread);while(true){sockaddr_in addrRemote;int nAddrLen = sizeof(addrRemote);SOCKET sClient = accept(s,(sockaddr*)&addrRemote,&nAddrLen);if(sClient != SOCKET_ERROR){EnterCriticalSection(&g_cs);g_vecClients.push_back(sClient);LeaveCriticalSection(&g_cs);if(g_vecClients.size() == 1)SetEvent(g_StartEvent);}else if(WSAEWOULDBLOCK == WSAGetLastError()){printf(".");Sleep(100);}else{printf("accept failed! %d\n",WSAGetLastError());}}closesocket(s);WSACleanup();CloseHandle(g_StartEvent);DeleteCriticalSection(&g_cs);}
三,select模型
[cpp] view plain copy print ? #include <WinSock2.h> #include <Windows.h> #include <MSWSock.h> #include <stdio.h> #include <map> using