呵呵,偷了點懶,由於本文和上一篇文章的目的是重構網絡程序的代碼,所以這裏只重構了Winsocket入門教程一:多線程阻塞式服務器和阻塞式客戶端程序(TCP)服務器程序的網絡部分,關於多線程部分的處理,起參考以上鍊接。
// 此示例程序參考MSDN Winsocket Server Demo // 示例和自己處理Winsocket程序的經驗而成 // 不足和錯誤之處望大家指正 // 原始DEMO地址:http://msdn.microsoft.com/zh-cn/library/ms737593%28v=VS.85%29.aspx // 如果在引用WinSock2.h的同時需要引用Windows.h // 必須定義下列宏。因爲Windows.h中包含了WinSock.h // 如果不定義下列宏,會引起數據結構和函數重定義的問題 #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include <cstdio> #include <cstdlib> #include <Windows.h> #include <WinSock2.h> #include <WS2tcpip.h> #include <IPHlpApi.h> // Winsocket程序需要鏈接ws2_32.lib #pragma comment(lib, "ws2_32.lib") int main() { int iRet = 0; WSADATA wd; // Winsocket程序必須要調用WSAStartup對 // ws2_32.dll的初始化,否則下面的函數調用 // 均會失敗 iRet = WSAStartup(MAKEWORD(2, 2), &wd); if (SOCKET_ERROR == iRet) { printf("Call WSAStartup failed, errno = %d./r/n", WSAGetLastError()); goto error_handle1; } // 使用getaddrinfo創建進行監聽(listen)所需要的地址 addrinfo aiInst; addrinfo *paiResult = NULL; addrinfo *paiThis = NULL; ZeroMemory(&aiInst, sizeof(aiInst)); aiInst.ai_flags = AI_NUMERICHOST; // 使用IP地址,不使用名稱解析 aiInst.ai_family = AF_INET; // 協議族:IPV4 aiInst.ai_socktype = SOCK_STREAM; // 流式套接字 aiInst.ai_protocol = IPPROTO_TCP; // TCP傳輸 const char *pszHost = "127.0.0.1"; const char *pszService = "10001"; // getaddrinfo根據填入的參數 // 可能會生成一個帶有多個地址的鏈表 // 注意必須釋放paiResult iRet = getaddrinfo(pszHost, pszService, &aiInst, &paiResult); if (SOCKET_ERROR == iRet) { printf("call getaddrinfo failed, errno = %d./r/n", WSAGetLastError()); goto error_handle2; } // 由協議族、套接字類型以及協議類型創建所需要的套接字 // 以上三個參數決定了創建套接字的特性。該特性決定了連 // 接的屬性。例如AF_INET、SOCK_STREAM、IPPROTO_TCP // 創建的套接字擁有TCP連接屬性。即由該套接字創建的連接 // 爲TCP連接 SOCKET sktListen = INVALID_SOCKET; for (paiThis = paiResult; NULL != paiThis; paiThis = paiThis->ai_next) { sktListen = socket(paiResult->ai_family, paiResult->ai_socktype, paiResult->ai_protocol); if (INVALID_SOCKET == sktListen) { printf("call socket failed, errno = %d./r/n", WSAGetLastError()); goto error_handle3; } // 在進行監聽之前,必須將所創建的套接字和本地系統進行綁定 // 以接收來自網絡上發往系統的消息 iRet = bind(sktListen,paiResult->ai_addr, (int)paiResult->ai_addrlen); if (SOCKET_ERROR == iRet) { printf("call bind failed, errno = %d./r/n", WSAGetLastError()); closesocket(sktListen); sktListen = INVALID_SOCKET; continue; } break; } if (INVALID_SOCKET == sktListen) { goto error_handle3; } // 監聽來自網絡上的連接請求,SOMAXCONN代表套接字所能接受 // 的最大連接數 iRet = listen(sktListen, SOMAXCONN); if (SOCKET_ERROR == iRet) { printf("call listen failed, errno = %d./r/n", WSAGetLastError()); goto error_handle4; } printf("begin listening./r/n"); // 接收到了來自客戶端的請求,併爲該請求創建一個套接字 // 已接收或者向客戶端發送數據 SOCKET sktAccept = accept(sktListen, NULL, NULL); if (INVALID_SOCKET == sktAccept) { printf("call listen failed, errno = %d./r/n", WSAGetLastError()); goto error_handle4; } // 接收來自客戶端的數據 // 如果消息過長,則需要多次接收 // 這裏展示一種簡單的接收方法 const int iRcvLen = 511; char szRcvBuf[iRcvLen + 1]; // 0結束符 do { iRet = recv(sktAccept, szRcvBuf, iRcvLen, 0); szRcvBuf[iRet] = 0; if (0 < iRet) { printf("%d bytes received: %s./r/n", iRet, szRcvBuf); } else if (0 == iRet) { printf("All data received./r/n"); } else { printf("call recv failed, errno = %d./r/n", WSAGetLastError()); goto error_handle5; } } while (0 < iRet); // 如果發送的消息過長 // 可能需要發送多次才能發送完成 // 這裏展示一種簡單的發送方法 const char *pszSend = "Server recevied!!!"; int iSndLen = (int)strlen(pszSend); int iHadSnd = 0; do { iRet = send(sktAccept, pszSend + iHadSnd, iSndLen, 0); if (0 < iRet) { printf("%d bytes send./r/n", iRet); } else if (0 == iRet) { printf("All data send./r/n"); } else { printf("call send failed, errno = %d./r/n", WSAGetLastError()); goto error_handle5; } iHadSnd += iRet; iSndLen -= iRet; } while (0 < iRet); // 如果不再需要發送數據,則應該使用shutdown配合 // SD_SEND關閉該套接字的發送功能,以減少客戶端的 // 資源佔用 iRet = shutdown(sktAccept, SD_SEND); if (SOCKET_ERROR == iRet) { printf("call shutdown failed, errno = %d./r/n", WSAGetLastError()); } error_handle5: closesocket(sktAccept); sktAccept = INVALID_SOCKET; error_handle4: closesocket(sktListen); sktListen = INVALID_SOCKET; error_handle3: freeaddrinfo(paiResult); // 釋放連接地址 paiResult = NULL; error_handle2: WSACleanup(); // 清理ws2_32.dll相關資源 error_handle1: system("pause"); return iRet; return 0; }