基於select非阻塞模型的服務端程式示例(Winsock2實現)
阿新 • • 發佈:2018-12-02
/* 總結: ①無論阻塞還是非阻塞,select都不會立即返回,select就是用於非阻塞模型中的。 ②將SOCKET置於非阻塞模式下時,處理連線或處理收發資料的Socket API都會立即返回。 ③select會監視fd_set中的所有套接字,一旦有套接字發生IO事件(包括客戶端的連線請求),select會立即返回, 並將fd_set中沒有發生IO事件的套接字移除。由此可見,如果想讓程式監視所有套接字的IO事件,應用程式應該儲存所有的 套接字控制代碼,並在呼叫select之前將所有的套接字新增到fd_set中。 ④阻塞模式下的accept呼叫返回的是阻塞模式的SOCKET,非阻塞模式返回的是非阻塞模式的SOCKET。 ⑤對於連線請求和recv的套接字,應將其置於readSet中,即select中的第二個引數。 */ #define _CRT_SECURE_NO_WARNINGS #ifdef UNICODE #undef UNICODE #endif #ifdef _UNICODE #undef _UNICODE #endif #include <stdio.h> #include <tchar.h> #include <WinSock2.h> #pragma comment(lib,"ws2_32.lib") void ShowSystemError(DWORD dwError) { HLOCAL hlocal = NULL; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), (PTSTR)&hlocal, 0, NULL); _tprintf(_T("%s\n"), (TCHAR*)LocalLock(hlocal)); LocalFree(hlocal); } struct Node { SOCKET sc; SOCKADDR_IN addr; Node* next; Node* prev; }; int _tmain(int argc,TCHAR* argv[]) { int nRet = 0; WSADATA wsadata; nRet = WSAStartup(MAKEWORD(2, 2), &wsadata); if (nRet != 0) { printf("WSAStartup呼叫失敗,錯誤資訊:"); ShowSystemError(nRet); getchar(); return -1; } SOCKET serverSocket = INVALID_SOCKET; serverSocket = socket(AF_INET, SOCK_STREAM, 0); if (serverSocket == INVALID_SOCKET) { printf("socket呼叫失敗,錯誤資訊:"); ShowSystemError(WSAGetLastError()); goto END; } SOCKADDR_IN serverAddr; ZeroMemory(&serverAddr, sizeof(serverAddr)); serverAddr.sin_family = AF_INET; serverAddr.sin_addr.S_un.S_addr = INADDR_ANY; serverAddr.sin_port = htons(8888); if (bind(serverSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) != 0) { printf("bind呼叫失敗,錯誤資訊:"); ShowSystemError(WSAGetLastError()); goto END; } if (listen(serverSocket, 5) != 0) { printf("listen呼叫失敗,錯誤資訊:"); ShowSystemError(WSAGetLastError()); goto END; } Node* pHead = new Node; pHead->prev = pHead->next = NULL; Node* pNewNode = new Node; pNewNode->sc = serverSocket; memcpy(&pNewNode->addr,&serverAddr,sizeof(SOCKADDR_IN)); pNewNode->prev = pHead; pNewNode->next=pHead->next; pHead->next = pNewNode; FD_SET readSet; SOCKET clientSocket; SOCKADDR_IN clientAddr; int nAddrLen; Node* p; Node* pTemp; char recvBuf[1024]; while (true) { FD_ZERO(&readSet); for (p = pHead->next; p != NULL; p = p->next) FD_SET(p->sc,&readSet); nRet = select(0,&readSet,NULL,NULL,NULL); if (nRet < 0) { printf("select呼叫失敗,錯誤資訊:"); ShowSystemError(WSAGetLastError()); continue; } for (p = pHead->next; p != NULL; p = p->next) { if (FD_ISSET(p->sc, &readSet)) { if (p->sc == serverSocket) { clientSocket = INVALID_SOCKET; ZeroMemory(&clientAddr,sizeof(SOCKADDR_IN)); nAddrLen = sizeof(SOCKADDR_IN); clientSocket = accept(serverSocket,(SOCKADDR*)&clientAddr,&nAddrLen); if (clientSocket == INVALID_SOCKET) { printf("accept呼叫失敗,錯誤資訊:"); ShowSystemError(WSAGetLastError()); continue; } printf("%s:%d的客戶成功連線到伺服器。\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port)); pNewNode = new Node; pNewNode->sc = clientSocket; memcpy(&pNewNode->addr, &clientAddr, sizeof(SOCKADDR_IN)); pNewNode->prev = pHead; pNewNode->next = pHead->next; pHead->next->prev = pNewNode; pHead->next = pNewNode; } else { ZeroMemory(recvBuf,sizeof(recvBuf)); nRet = recv(p->sc,recvBuf,sizeof(recvBuf),0); if (nRet < 0) { printf("recv呼叫失敗,錯誤資訊:"); ShowSystemError(WSAGetLastError()); if (closesocket(p->sc) != 0) { printf("closesocket呼叫失敗,錯誤資訊:"); ShowSystemError(WSAGetLastError()); } pTemp=p->prev; p->prev->next = p->next; p->next->prev = p->prev; delete p; p = pTemp; } else if (nRet == 0) { printf("%s:%d的客戶主動關閉連線。\n", inet_ntoa(p->addr.sin_addr), ntohs(p->addr.sin_port)); if (closesocket(p->sc) != 0) { printf("closesocket呼叫失敗,錯誤資訊:"); ShowSystemError(WSAGetLastError()); } pTemp = p->prev; p->prev->next = p->next; p->next->prev = p->prev; delete p; p = pTemp; } else { //解析資料 printf("%s:%d的客戶傳送:%s\n", inet_ntoa(p->addr.sin_addr), ntohs(p->addr.sin_port),recvBuf); } } } } } END: if (serverSocket != INVALID_SOCKET) { if (closesocket(serverSocket) != 0) { printf("closesocket呼叫失敗,錯誤資訊:"); ShowSystemError(WSAGetLastError()); } } if (WSACleanup() != 0) { printf("WSACleanup呼叫失敗,錯誤資訊:"); ShowSystemError(WSAGetLastError()); } getchar(); return 0; }