呵呵,偷了点懒,由于本文和上一篇文章的目的是重构网络程序的代码,所以这里只重构了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; }