Windows Socket 网络编程基础

来源:互联网 发布:军事小说 知乎 编辑:程序博客网 时间:2024/06/09 22:25

前言


近期学习了Windows平台下Socket开发的2中模式和5种I/O模型,以下是介绍这几种开发模式所要使用接口的核心示例,示例只有一个功能,客户端发送输入的字符串,服务接收后转为大写发送给客户端。


Socket基础


我理解的Socket是:如果以五层传输协议为基础,Socket就是应用层访问传输层的接口。


服务器流程:WSAStartup->Create Socket->bind->listen->accept->recv/send->close


客户端流程:WSAStartup->Create Socket->connect->recv/send->close


流程上很简单,但为什么Windows要提供那么多种开发模式楠。我们以recv为例,在调用recv的时候大致会有2个过程:1.内核等待数据;2.将数据复制到用户空间。这2个过程可以是同步或异步的,要实现高效的访问,需要你根据自己的设计选取不同的接口。所以核心是需要考虑accept,connect,recv,send这些接口是使用同步还是异步的。


阻塞模式


套接字在执行操作时,调用函数在没有完成操作之前不会立即返回的工作模式。

客户端
这里只提供一个客户端示例,均可以同所有的服务器示例配合使用。
#define _WINSOCK_DEPRECATED_NO_WARNINGS#include <cstdio>#include <iostream>#include <string>#include <WinSock2.h>#include <list>#include <thread>#pragma comment(lib, "WS2_32.lib")#define BUF_SIZE 128SOCKET mHost;char mInputBuf[BUF_SIZE];//接收数据缓冲区unsigned short mSendHeader;//包头char mSendPacket[BUF_SIZE];//包体unsigned short mRecvHeader;//包头char mRecvPacket[BUF_SIZE];//包体void CleanUpEnvironment(){if (INVALID_SOCKET != mHost){closesocket(mHost);}WSACleanup();}int main(){#pragma region 初始化网络环境//初始化socket动态库WSADATAmWsd;if (WSAStartup(MAKEWORD(2, 2), &mWsd) != 0){printf("WSAStartup failed!!!\n");return -1;}//初始化socketmHost = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (INVALID_SOCKET == mHost){printf_s("new socket error : %d\n", WSAGetLastError());CleanUpEnvironment();return -1;}//链接服务器SOCKADDR_INservAddr;servAddr.sin_family = AF_INET;servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");servAddr.sin_port = htons((short)21234);int nServAddlen = sizeof(servAddr);if (INVALID_SOCKET == connect(mHost, (LPSOCKADDR)&servAddr, sizeof(servAddr))){printf_s("new socket connect error : %d\n", WSAGetLastError());CleanUpEnvironment();return -1;}#pragma endregion#pragma region 发送接收循环while (true){//resetZeroMemory(mInputBuf, sizeof(mInputBuf));ZeroMemory(mSendPacket, sizeof(mSendPacket));ZeroMemory(mRecvPacket, sizeof(mRecvPacket));//输入printf_s("Input:\n");gets_s(mInputBuf);if (mInputBuf[0] == '/' && mInputBuf[1] == '/' && mInputBuf[2] == 'q'){CleanUpEnvironment();return 1;}int endIndex = 0;for (int m = 0;m < sizeof(mInputBuf);++m){if (mInputBuf[m] == '\0'){endIndex = m;break;}}memcpy(mSendPacket, mInputBuf, endIndex);//发送包头mSendHeader = endIndex;int retVal = send(mHost, (char*)&mSendHeader, sizeof(mSendHeader), 0);if (INVALID_SOCKET == retVal){printf_s("send header failed!!!\n");CleanUpEnvironment();break;}//发送包体retVal = send(mHost, mSendPacket, mSendHeader, 0);if (INVALID_SOCKET == retVal){printf_s("send pack failed!!!\n");CleanUpEnvironment();break;}//接收包头retVal = recv(mHost, (char*)&mRecvHeader, sizeof(mRecvHeader), 0);if (INVALID_SOCKET == retVal){printf_s("recv header failed!!!\n");CleanUpEnvironment();break;}//接收包体retVal = recv(mHost, mRecvPacket, mRecvHeader, 0);if (INVALID_SOCKET == retVal){printf_s("recv pack failed!!!\n");CleanUpEnvironment();break;}printf_s("Output :\n%s\n", mRecvPacket);}#pragma endregionreturn 1;}
服务器

只能维护一个连接。
#define _WINSOCK_DEPRECATED_NO_WARNINGS#include <cstdio>#include <iostream>#include <string>#include <algorithm>#include <WinSock2.h>#pragma comment(lib, "WS2_32.lib")#define BUF_SIZE 128SOCKETmListenSocket;//监听套接字unsigned short mSendHeader;//包头char mSendPacket[BUF_SIZE];//包体unsigned short mRecvHeader;//包头char mRecvPacket[BUF_SIZE];//包体void CleanUpEnvironment(){if (INVALID_SOCKET != mListenSocket){closesocket(mListenSocket);}WSACleanup();}int main(){//初始化socket动态库WSADATAmWsd;if (0 != WSAStartup(MAKEWORD(2, 2), &mWsd)){printf_s("WSAStartup failed!\n");return -1;}//初始化服务器socketmListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (INVALID_SOCKET == mListenSocket){printf_s("init server socket failed!\n");CleanUpEnvironment();return -1;}//绑定服务器socketSOCKADDR_IN mServerAddr;mServerAddr.sin_family = AF_INET;mServerAddr.sin_port = htons(21234);mServerAddr.sin_addr.s_addr = INADDR_ANY;if (SOCKET_ERROR == bind(mListenSocket, (LPSOCKADDR)&mServerAddr, sizeof(SOCKADDR_IN))){printf_s("server socket bind failed!\n");CleanUpEnvironment();return -1;}//监听if (SOCKET_ERROR == listen(mListenSocket, SOMAXCONN)){printf_s("server socket listen failed!\n");CleanUpEnvironment();return -1;}//接受客户端链接printf_s("server begin accept client...\n");sockaddr_in addrClient;int addrClientlen = sizeof(addrClient);SOCKET clientSocket = accept(mListenSocket, (sockaddr FAR*)&addrClient, &addrClientlen);if (SOCKET_ERROR == clientSocket){printf_s("server socket accept failed!\n");CleanUpEnvironment();return -1;}printf_s("accept new client,IP : %sPort : %d\n", inet_ntoa(addrClient.sin_addr), ntohs(addrClient.sin_port));while (true){//resetZeroMemory(mSendPacket, sizeof(mSendPacket));ZeroMemory(mRecvPacket, sizeof(mRecvPacket));//接收包头int retVal = recv(clientSocket, (char*)&mRecvHeader, sizeof(mRecvHeader), 0);if (INVALID_SOCKET == retVal){printf_s("recv header failed!!!\n");CleanUpEnvironment();break;}//接收包体retVal = recv(clientSocket, mRecvPacket, mRecvHeader, 0);if (INVALID_SOCKET == retVal){printf_s("recv pack failed!!!\n");CleanUpEnvironment();break;}int endIndex = 0;for (int m = 0;m < sizeof(mRecvPacket);++m){if (mRecvPacket[m] == '\0'){endIndex = m;break;}}std::transform(mRecvPacket, mRecvPacket + endIndex, mSendPacket, towupper);//发送包头mSendHeader = endIndex;retVal = send(clientSocket, (char*)&mSendHeader, sizeof(mSendHeader), 0);if (INVALID_SOCKET == retVal){printf_s("send header failed!!!\n");CleanUpEnvironment();break;}//发送包体retVal = send(clientSocket, mSendPacket, mSendHeader, 0);if (INVALID_SOCKET == retVal){printf_s("send pack failed!!!\n");CleanUpEnvironment();break;}printf_s("send success : %s\n", mSendPacket);}return 1;}
通过为每个连接开启一个线程来维护多个链接。
#define _WINSOCK_DEPRECATED_NO_WARNINGS#include <cstdio>#include <iostream>#include <string>#include <algorithm>#include <WinSock2.h>#include <thread>#include <mutex>#include <list>#pragma comment(lib, "WS2_32.lib")#define BUF_SIZE 128bool mTerminated = false;SOCKETmListenSocket;//监听套接字class Client;std::list<Client*> mClientSockets;void InputThreadCmd();void acceptThreadCmd();void CleanUpEnvironment(){if (INVALID_SOCKET != mListenSocket){closesocket(mListenSocket);}WSACleanup();}void mainThread(){#pragma region 初始化网络环境//初始化socket动态库WSADATAmWsd;if (0 != WSAStartup(MAKEWORD(2, 2), &mWsd)){printf_s("WSAStartup failed!\n");return;}//初始化服务器socketmListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (INVALID_SOCKET == mListenSocket){printf_s("init server socket failed!\n");CleanUpEnvironment();return;}//设置监听套接字为非阻塞模式u_long ulUnBlocking = 1;int ret = ioctlsocket(mListenSocket, FIONBIO, &ulUnBlocking);if (SOCKET_ERROR == ret){printf("server socket ioctlsocket failed\n");CleanUpEnvironment();return;}//绑定服务器socketSOCKADDR_IN mServerAddr;mServerAddr.sin_family = AF_INET;mServerAddr.sin_port = htons(21234);mServerAddr.sin_addr.s_addr = INADDR_ANY;if (SOCKET_ERROR == bind(mListenSocket, (LPSOCKADDR)&mServerAddr, sizeof(SOCKADDR_IN))){printf_s("server socket bind failed!\n");CleanUpEnvironment();return;}//监听if (SOCKET_ERROR == listen(mListenSocket, SOMAXCONN)){printf_s("server socket listen failed!\n");return;}#pragma endregion#pragma region 开启线程printf_s("server star success!!!\n");std::thread acceptThread(acceptThreadCmd);acceptThread.detach();std::thread inputThread(InputThreadCmd);inputThread.detach();#pragma endregionchar s[128];while (!mTerminated){sprintf_s(s, "OnLine: %d\n", mClientSockets.size());::SetConsoleTitle(s);std::this_thread::sleep_for(std::chrono::milliseconds(1));}}void InputThreadCmd(){while (true){std::string cmdLine;if (getline(std::cin, cmdLine)){if (cmdLine == "//q"){mTerminated = true;}}}}class Client{public:Client(SOCKET s):mSocket(s){std::thread handleCmd(HandleCmd, this);handleCmd.detach();}~Client(){closesocket(mSocket);}static void HandleCmd(Client* client){while (!mTerminated){//resetZeroMemory(client->mSendPacket, sizeof(client->mSendPacket));ZeroMemory(client->mRecvPacket, sizeof(client->mRecvPacket));int retVal, errorCode;//接收包头while (true){retVal = recv(client->mSocket, (char*)&client->mRecvHeader, sizeof(client->mRecvHeader), 0);if (INVALID_SOCKET == retVal){errorCode = WSAGetLastError();if (WSAEWOULDBLOCK == errorCode){continue;}else{printf_s("recv header failed!!!\n");CleanUpEnvironment();return;}}break;}//接收包体while (true){retVal = recv(client->mSocket, client->mRecvPacket, client->mRecvHeader, 0);if (INVALID_SOCKET == retVal){errorCode = WSAGetLastError();if (WSAEWOULDBLOCK == errorCode){continue;}else{printf_s("recv pack failed!!!\n");CleanUpEnvironment();return;}}break;}int endIndex = 0;for (int m = 0; m < sizeof(client->mRecvPacket); ++m){if (client->mRecvPacket[m] == '\0'){endIndex = m;break;}}std::transform(client->mRecvPacket, client->mRecvPacket + endIndex, client->mSendPacket, towupper);//发送包头client->mSendHeader = endIndex;while (true){retVal = send(client->mSocket, (char*)&client->mSendHeader, sizeof(client->mSendHeader), 0);if (INVALID_SOCKET == retVal){errorCode = WSAGetLastError();if (WSAEWOULDBLOCK == errorCode){continue;}else{printf_s("send header failed!!!\n");CleanUpEnvironment();return;}}break;}//发送包体while (true){retVal = send(client->mSocket, client->mSendPacket, client->mSendHeader, 0);if (INVALID_SOCKET == retVal){errorCode = WSAGetLastError();if (WSAEWOULDBLOCK == errorCode){continue;}else{printf_s("send pack failed!!!\n");CleanUpEnvironment();return;}}break;}printf_s("send success : %s\n", client->mSendPacket);}}SOCKET mSocket;unsigned short mRecvHeader;//包头char mRecvPacket[BUF_SIZE];//包体unsigned short mSendHeader;//包头char mSendPacket[BUF_SIZE];//包体};//接收客户端链接线程void acceptThreadCmd(){printf_s("server begin accept client...\n");sockaddr_in addrClient;int addrClientlen = sizeof(addrClient);while (!mTerminated){SOCKET clientSocket = accept(mListenSocket, (sockaddr FAR*)&addrClient, &addrClientlen);if (SOCKET_ERROR != clientSocket){Client * client = new Client(clientSocket);mClientSockets.push_back(client);printf_s("accept new client,IP : %sPort : %d\n", inet_ntoa(addrClient.sin_addr), ntohs(addrClient.sin_port));}SleepEx(100, true);}while (mClientSockets.size() > 0){delete mClientSockets.front();mClientSockets.pop_front();}}int main(){mainThread();return 1;}

非阻塞模式


套接字在执行操作时,调用函数不管操作是否完成都会立即返回的工作模式。通过接口ioctlsocket设置socket为非阻塞模式。
#define _WINSOCK_DEPRECATED_NO_WARNINGS#include <cstdio>#include <iostream>#include <string>#include <algorithm>#include <WinSock2.h>#include <thread>#include <mutex>#include <list>#pragma comment(lib, "WS2_32.lib")#define BUF_SIZE 128bool mTerminated = false;SOCKETmListenSocket;//监听套接字class Client;std::list<Client*> mClientSockets;void InputThreadCmd();void acceptThreadCmd();void CleanUpEnvironment(){if (INVALID_SOCKET != mListenSocket){closesocket(mListenSocket);}WSACleanup();}void mainThread(){#pragma region 初始化网络环境//初始化socket动态库WSADATAmWsd;if (0 != WSAStartup(MAKEWORD(2, 2), &mWsd)){printf_s("WSAStartup failed!\n");return;}//初始化服务器socketmListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (INVALID_SOCKET == mListenSocket){printf_s("init server socket failed!\n");CleanUpEnvironment();return;}//设置监听套接字为非阻塞模式u_long ulUnBlocking = 1;int ret = ioctlsocket(mListenSocket, FIONBIO, &ulUnBlocking);if (SOCKET_ERROR == ret){printf("server socket ioctlsocket failed\n");CleanUpEnvironment();return;}//绑定服务器socketSOCKADDR_IN mServerAddr;mServerAddr.sin_family = AF_INET;mServerAddr.sin_port = htons(21234);mServerAddr.sin_addr.s_addr = INADDR_ANY;if (SOCKET_ERROR == bind(mListenSocket, (LPSOCKADDR)&mServerAddr, sizeof(SOCKADDR_IN))){printf_s("server socket bind failed!\n");CleanUpEnvironment();return;}//监听if (SOCKET_ERROR == listen(mListenSocket, SOMAXCONN)){printf_s("server socket listen failed!\n");return;}#pragma endregion#pragma region 开启线程printf_s("server star success!!!\n");std::thread acceptThread(acceptThreadCmd);acceptThread.detach();std::thread inputThread(InputThreadCmd);inputThread.detach();#pragma endregionchar s[128];while (!mTerminated){sprintf_s(s, "OnLine: %d\n", mClientSockets.size());::SetConsoleTitle(s);std::this_thread::sleep_for(std::chrono::milliseconds(1));}}void InputThreadCmd(){while (true){std::string cmdLine;if (getline(std::cin, cmdLine)){if (cmdLine == "//q"){mTerminated = true;}}}}class Client{public:Client(SOCKET s):mSocket(s){std::thread handleCmd(HandleCmd, this);handleCmd.detach();}~Client(){closesocket(mSocket);}static void HandleCmd(Client* client){while (!mTerminated){//resetZeroMemory(client->mSendPacket, sizeof(client->mSendPacket));ZeroMemory(client->mRecvPacket, sizeof(client->mRecvPacket));int retVal, errorCode;//接收包头while (true){retVal = recv(client->mSocket, (char*)&client->mRecvHeader, sizeof(client->mRecvHeader), 0);if (INVALID_SOCKET == retVal){errorCode = WSAGetLastError();if (WSAEWOULDBLOCK == errorCode){continue;}else{printf_s("recv header failed!!!\n");CleanUpEnvironment();return;}}break;}//接收包体while (true){retVal = recv(client->mSocket, client->mRecvPacket, client->mRecvHeader, 0);if (INVALID_SOCKET == retVal){errorCode = WSAGetLastError();if (WSAEWOULDBLOCK == errorCode){continue;}else{printf_s("recv pack failed!!!\n");CleanUpEnvironment();return;}}break;}int endIndex = 0;for (int m = 0; m < sizeof(client->mRecvPacket); ++m){if (client->mRecvPacket[m] == '\0'){endIndex = m;break;}}std::transform(client->mRecvPacket, client->mRecvPacket + endIndex, client->mSendPacket, towupper);//发送包头client->mSendHeader = endIndex;while (true){retVal = send(client->mSocket, (char*)&client->mSendHeader, sizeof(client->mSendHeader), 0);if (INVALID_SOCKET == retVal){errorCode = WSAGetLastError();if (WSAEWOULDBLOCK == errorCode){continue;}else{printf_s("send header failed!!!\n");CleanUpEnvironment();return;}}break;}//发送包体while (true){retVal = send(client->mSocket, client->mSendPacket, client->mSendHeader, 0);if (INVALID_SOCKET == retVal){errorCode = WSAGetLastError();if (WSAEWOULDBLOCK == errorCode){continue;}else{printf_s("send pack failed!!!\n");CleanUpEnvironment();return;}}break;}printf_s("send success : %s\n", client->mSendPacket);}}SOCKET mSocket;unsigned short mRecvHeader;//包头char mRecvPacket[BUF_SIZE];//包体unsigned short mSendHeader;//包头char mSendPacket[BUF_SIZE];//包体};//接收客户端链接线程void acceptThreadCmd(){printf_s("server begin accept client...\n");sockaddr_in addrClient;int addrClientlen = sizeof(addrClient);while (!mTerminated){SOCKET clientSocket = accept(mListenSocket, (sockaddr FAR*)&addrClient, &addrClientlen);if (SOCKET_ERROR != clientSocket){Client * client = new Client(clientSocket);mClientSockets.push_back(client);printf_s("accept new client,IP : %sPort : %d\n", inet_ntoa(addrClient.sin_addr), ntohs(addrClient.sin_port));}SleepEx(100, true);}while (mClientSockets.size() > 0){delete mClientSockets.front();mClientSockets.pop_front();}}int main(){mainThread();return 1;}


Select I/O模型


通过Select接口可以最多同时管理64个链接,多余的链接需要新开线程处理。Select接口是阻塞的并且数据拷贝到用户空间也是阻塞的。
#define _WINSOCK_DEPRECATED_NO_WARNINGS#include <cstdio>#include <iostream>#include <string>#include <algorithm>#include <WinSock2.h>#include <thread>#include <mutex>#include <list>#pragma comment(lib, "WS2_32.lib")#define BUF_SIZE 128bool mTerminated = false;SOCKETmListenSocket;//监听套接字class Client;std::list<Client*> mClientSockets;void InputThreadCmd();void selectThreadCmd();void CleanUpEnvironment(){if (INVALID_SOCKET != mListenSocket){closesocket(mListenSocket);}WSACleanup();}void mainThread(){#pragma region 初始化网络环境//初始化socket动态库WSADATAmWsd;if (0 != WSAStartup(MAKEWORD(2, 2), &mWsd)){printf_s("WSAStartup failed!\n");return;}//初始化服务器socketmListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (INVALID_SOCKET == mListenSocket){printf_s("init server socket failed!\n");CleanUpEnvironment();return;}//绑定服务器socketSOCKADDR_IN mServerAddr;mServerAddr.sin_family = AF_INET;mServerAddr.sin_port = htons(21234);mServerAddr.sin_addr.s_addr = INADDR_ANY;if (SOCKET_ERROR == bind(mListenSocket, (LPSOCKADDR)&mServerAddr, sizeof(SOCKADDR_IN))){printf_s("server socket bind failed!\n");CleanUpEnvironment();return;}//监听if (SOCKET_ERROR == listen(mListenSocket, SOMAXCONN)){printf_s("server socket listen failed!\n");return;}#pragma endregion#pragma region 开启线程printf_s("server star success!!!\n");std::thread acceptThread(selectThreadCmd);acceptThread.detach();std::thread inputThread(InputThreadCmd);inputThread.detach();#pragma endregionchar s[128];while (!mTerminated){sprintf_s(s, "OnLine: %d\n", mClientSockets.size());::SetConsoleTitle(s);std::this_thread::sleep_for(std::chrono::milliseconds(1));}}void InputThreadCmd(){while (true){std::string cmdLine;if (getline(std::cin, cmdLine)){if (cmdLine == "//q"){mTerminated = true;}}}}class Client{public:Client(SOCKET s):mSocket(s),mRecvType(Head),mRecvHeader(0),mSendType(None),mSendHeader(0){ZeroMemory(mRecvPacket, sizeof(mRecvPacket));ZeroMemory(mSendPacket, sizeof(mSendPacket));}~Client(){closesocket(mSocket);}enum MsgType{None,Head,Pack};void handleRecv(){if (Head == mRecvType){recv(mSocket, (char*)&mRecvHeader, sizeof(mRecvHeader), 0);mRecvType = Pack;}else if(Pack == mRecvType){ZeroMemory(mRecvPacket, sizeof(mRecvPacket));recv(mSocket, mRecvPacket, mRecvHeader, 0);int endIndex = 0;for (int m = 0; m < sizeof(mRecvPacket); ++m){if (mRecvPacket[m] == '\0'){endIndex = m;break;}}std::transform(mRecvPacket, mRecvPacket + endIndex, mSendPacket, towupper);mSendHeader = endIndex;mRecvType = None;mSendType = Head;}}void handleSend(){if (Head == mSendType){send(mSocket, (char*)&mSendHeader, sizeof(mSendHeader), 0);mSendType = Pack;}else if(Pack == mSendType){send(mSocket, mSendPacket, mSendHeader, 0);printf_s("send success : %s\n", mSendPacket);ZeroMemory(mSendPacket, sizeof(mSendPacket));mSendType = None;mRecvType = Head;}}SOCKET mSocket;MsgType mRecvType;unsigned short mRecvHeader;//包头char mRecvPacket[BUF_SIZE];//包体MsgType mSendType;unsigned short mSendHeader;//包头char mSendPacket[BUF_SIZE];//包体};void selectThreadCmd(){printf_s("server begin accept client...\n");FD_SET allSocket_fd;FD_ZERO(&allSocket_fd);FD_SET(mListenSocket, &allSocket_fd);FD_SET readSocket_fd;FD_SET writeSocket_fd;while (!mTerminated){FD_ZERO(&readSocket_fd);FD_ZERO(&writeSocket_fd);readSocket_fd = allSocket_fd;writeSocket_fd = allSocket_fd;int retVal = select(0, &readSocket_fd, &writeSocket_fd, NULL, NULL);if (retVal > 0){for (int m = 0;m < allSocket_fd.fd_count;++m){if (FD_ISSET(allSocket_fd.fd_array[m], &readSocket_fd)){//acceptif (allSocket_fd.fd_array[m] == mListenSocket){sockaddr_in addrClient;int addrClientlen = sizeof(addrClient);SOCKET clientSocket = accept(mListenSocket, (sockaddr FAR*)&addrClient, &addrClientlen);if (SOCKET_ERROR != clientSocket){Client * client = new Client(clientSocket);mClientSockets.push_back(client);printf_s("accept new client,IP : %sPort : %d\n", inet_ntoa(addrClient.sin_addr), ntohs(addrClient.sin_port));FD_SET(clientSocket, &allSocket_fd);}}//recvelse{std::list<Client*>::iterator iter = mClientSockets.begin();std::list<Client*>::iterator end = mClientSockets.end();for (; iter != end; ++iter){if ((*iter)->mSocket == allSocket_fd.fd_array[m]){(*iter)->handleRecv();}}}}//sendif (FD_ISSET(allSocket_fd.fd_array[m], &writeSocket_fd)){std::list<Client*>::iterator iter = mClientSockets.begin();std::list<Client*>::iterator end = mClientSockets.end();for (; iter != end; ++iter){if ((*iter)->mSocket == allSocket_fd.fd_array[m]){(*iter)->handleSend();}}}}}Sleep(100);}}int main(){mainThread();return 1;}

WSAAsyncSelect I/O模型


通过Win窗口的消息机制实现非阻塞调用,但数据复制到用户空间是阻塞的。
#define _WINSOCK_DEPRECATED_NO_WARNINGS#include <cstdio>#include <iostream>#include <string>#include <algorithm>#include <WinSock2.h>#include <thread>#include <mutex>#include <list>#pragma comment(lib, "WS2_32.lib")#define WM_SOCKET (WM_USER+1)#define BUF_SIZE 128HWND hWnd;bool mTerminated = false;SOCKETmListenSocket;//监听套接字class Client;std::list<Client*> mClientSockets;void CleanUpEnvironment(){if (INVALID_SOCKET != mListenSocket){closesocket(mListenSocket);}WSACleanup();}void mainThread(){#pragma region 初始化网络环境//初始化socket动态库WSADATAmWsd;if (0 != WSAStartup(MAKEWORD(2, 2), &mWsd)){printf_s("WSAStartup failed!\n");return;}//初始化服务器socketmListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (INVALID_SOCKET == mListenSocket){printf_s("init server socket failed!\n");CleanUpEnvironment();return;}//绑定服务器socketSOCKADDR_IN mServerAddr;mServerAddr.sin_family = AF_INET;mServerAddr.sin_port = htons(21234);mServerAddr.sin_addr.s_addr = INADDR_ANY;if (SOCKET_ERROR == bind(mListenSocket, (LPSOCKADDR)&mServerAddr, sizeof(SOCKADDR_IN))){printf_s("server socket bind failed!\n");CleanUpEnvironment();return;}WSAAsyncSelect(mListenSocket, hWnd, WM_SOCKET, FD_ACCEPT | FD_CLOSE);//监听if (SOCKET_ERROR == listen(mListenSocket, SOMAXCONN)){printf_s("server socket listen failed!\n");return;}#pragma endregionchar s[128];MSG msg;while (::GetMessage(&msg, NULL, 0, 0) && !mTerminated){sprintf_s(s, "OnLine: %d\n", mClientSockets.size());::SetConsoleTitle(s);::TranslateMessage(&msg);//转化键盘消息::DispatchMessage(&msg);//将消息发送到相应窗口函数}}class Client{public:Client(SOCKET s):mSocket(s),mRecvType(Head),mRecvHeader(0),mSendType(None),mSendHeader(0){ZeroMemory(mRecvPacket, sizeof(mRecvPacket));ZeroMemory(mSendPacket, sizeof(mSendPacket));}~Client(){closesocket(mSocket);}enum MsgType{None,Head,Pack};void handleRecv(){if (Head == mRecvType){recv(mSocket, (char*)&mRecvHeader, sizeof(mRecvHeader), 0);mRecvType = Pack;}else if (Pack == mRecvType){ZeroMemory(mRecvPacket, sizeof(mRecvPacket));recv(mSocket, mRecvPacket, mRecvHeader, 0);int endIndex = 0;for (int m = 0; m < sizeof(mRecvPacket); ++m){if (mRecvPacket[m] == '\0'){endIndex = m;break;}}std::transform(mRecvPacket, mRecvPacket + endIndex, mSendPacket, towupper);mSendHeader = endIndex;mRecvType = None;mSendType = Head;handleSend();}}void handleSend(){if (Head == mSendType){int retVal = send(mSocket, (char*)&mSendHeader, sizeof(mSendHeader), 0);mSendType = Pack;}if (Pack == mSendType){send(mSocket, mSendPacket, mSendHeader, 0);printf_s("send success : %s\n", mSendPacket);ZeroMemory(mSendPacket, sizeof(mSendPacket));mSendType = None;mRecvType = Head;}}SOCKET mSocket;MsgType mRecvType;unsigned short mRecvHeader;//包头char mRecvPacket[BUF_SIZE];//包体MsgType mSendType;unsigned short mSendHeader;//包头char mSendPacket[BUF_SIZE];//包体};LRESULT CALLBACK WinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){switch (uMsg){case WM_SOCKET:{SOCKET socket = wParam;if (WSAGETSELECTERROR(lParam)){::closesocket(socket);return 0;}switch (WSAGETSELECTEVENT(lParam)){case FD_ACCEPT:{sockaddr_in addrClient;int addrClientlen = sizeof(addrClient);SOCKET clientSocket = accept(socket, (sockaddr FAR*)&addrClient, &addrClientlen);if (SOCKET_ERROR != clientSocket){Client * client = new Client(clientSocket);mClientSockets.push_back(client);::WSAAsyncSelect(clientSocket, hWnd, WM_SOCKET, FD_READ | FD_WRITE | FD_CLOSE);printf_s("accept new client,IP : %sPort : %d\n", inet_ntoa(addrClient.sin_addr), ntohs(addrClient.sin_port));}}break;case FD_WRITE:{}break;case FD_READ:{std::list<Client*>::iterator iter = mClientSockets.begin();std::list<Client*>::iterator end = mClientSockets.end();for (; iter != end; ++iter){if ((*iter)->mSocket == socket){(*iter)->handleRecv();}}}break;case FD_CLOSE:{std::list<Client*>::iterator iter = mClientSockets.begin();std::list<Client*>::iterator end = mClientSockets.end();for (; iter != end; ++iter){if ((*iter)->mSocket == socket){delete *iter;mClientSockets.erase(iter);break;}}}break;}}}//不处理的消息交给系统默认处理return ::DefWindowProc(hWnd, uMsg, wParam, lParam);}int main(){#pragma region 创建窗口char szClassName[] = "SocketDemo";WNDCLASSEX wndclass;wndclass.cbSize = sizeof(wndclass);wndclass.style = CS_HREDRAW | CS_VREDRAW;wndclass.lpfnWndProc = WinProc;wndclass.cbWndExtra = 0;wndclass.cbClsExtra = 0;wndclass.hInstance = NULL;wndclass.hIcon = ::LoadIcon(NULL, IDI_APPLICATION);wndclass.hCursor = ::LoadCursor(NULL, IDC_ARROW);wndclass.hbrBackground = (HBRUSH)::GetStockObject(WHITE_BRUSH);wndclass.lpszMenuName = NULL;wndclass.lpszClassName = szClassName;wndclass.hIconSm = NULL;::RegisterClassEx(&wndclass);hWnd = ::CreateWindowEx(0, szClassName, "WSAAsyncSelect", WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,NULL,NULL);if (hWnd == NULL){::MessageBox(NULL, "创建窗口出错!", "error", MB_OK);return -1;}#pragma endregionmainThread();return 1;}

WSAEventSelect I/O模型


通过事件通知实现非阻塞调用,但数据复制到用户空间是阻塞的。并且一个WSAWaitForMultipleEvents接口最多管理64个事件,其余的需要新开线程。
#define _WINSOCK_DEPRECATED_NO_WARNINGS#include <cstdio>#include <iostream>#include <string>#include <algorithm>#include <WinSock2.h>#include <thread>#include <mutex>#include <list>#pragma comment(lib, "WS2_32.lib")#define BUF_SIZE 128bool mTerminated = false;SOCKETmListenSocket;//监听套接字SOCKETmAllSocket[WSA_MAXIMUM_WAIT_EVENTS];int mTotalEvent(0);WSAEVENT mAllEvent[WSA_MAXIMUM_WAIT_EVENTS];class Client;std::list<Client*> mClientSockets;void InputThreadCmd();void eventSelectThreadCmd();void CleanUpEnvironment(){if (INVALID_SOCKET != mListenSocket){closesocket(mListenSocket);}WSACleanup();}void mainThread(){#pragma region 初始化网络环境//初始化socket动态库WSADATAmWsd;if (0 != WSAStartup(MAKEWORD(2, 2), &mWsd)){printf_s("WSAStartup failed!\n");return;}//初始化服务器socketmListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (INVALID_SOCKET == mListenSocket){printf_s("init server socket failed!\n");CleanUpEnvironment();return;}mAllSocket[mTotalEvent] = mListenSocket;mAllEvent[mTotalEvent] = WSACreateEvent();WSAEventSelect(mAllSocket[mTotalEvent], mAllEvent[mTotalEvent], FD_ACCEPT | FD_CLOSE);mTotalEvent++;//绑定服务器socketSOCKADDR_IN mServerAddr;mServerAddr.sin_family = AF_INET;mServerAddr.sin_port = htons(21234);mServerAddr.sin_addr.s_addr = INADDR_ANY;if (SOCKET_ERROR == bind(mListenSocket, (LPSOCKADDR)&mServerAddr, sizeof(SOCKADDR_IN))){printf_s("server socket bind failed!\n");CleanUpEnvironment();return;}//监听if (SOCKET_ERROR == listen(mListenSocket, SOMAXCONN)){printf_s("server socket listen failed!\n");return;}#pragma endregion#pragma region 开启线程printf_s("server star success!!!\n");std::thread acceptThread(eventSelectThreadCmd);acceptThread.detach();std::thread inputThread(InputThreadCmd);inputThread.detach();#pragma endregionchar s[128];while (!mTerminated){sprintf_s(s, "OnLine: %d\n", mClientSockets.size());::SetConsoleTitle(s);std::this_thread::sleep_for(std::chrono::milliseconds(1));}}void InputThreadCmd(){while (true){std::string cmdLine;if (getline(std::cin, cmdLine)){if (cmdLine == "//q"){mTerminated = true;}}}}class Client{public:Client(SOCKET s):mSocket(s),mRecvType(Head),mRecvHeader(0),mSendType(None),mSendHeader(0){ZeroMemory(mRecvPacket, sizeof(mRecvPacket));ZeroMemory(mSendPacket, sizeof(mSendPacket));}~Client(){closesocket(mSocket);}enum MsgType{None,Head,Pack};void handleRecv(){if (Head == mRecvType){recv(mSocket, (char*)&mRecvHeader, sizeof(mRecvHeader), 0);mRecvType = Pack;}else if (Pack == mRecvType){ZeroMemory(mRecvPacket, sizeof(mRecvPacket));recv(mSocket, mRecvPacket, mRecvHeader, 0);int endIndex = 0;for (int m = 0; m < sizeof(mRecvPacket); ++m){if (mRecvPacket[m] == '\0'){endIndex = m;break;}}std::transform(mRecvPacket, mRecvPacket + endIndex, mSendPacket, towupper);mSendHeader = endIndex;mRecvType = None;mSendType = Head;handleSend();}}void handleSend(){if (Head == mSendType){int retVal = send(mSocket, (char*)&mSendHeader, sizeof(mSendHeader), 0);mSendType = Pack;}if (Pack == mSendType){send(mSocket, mSendPacket, mSendHeader, 0);printf_s("send success : %s\n", mSendPacket);ZeroMemory(mSendPacket, sizeof(mSendPacket));mSendType = None;mRecvType = Head;}}SOCKET mSocket;MsgType mRecvType;unsigned short mRecvHeader;//包头char mRecvPacket[BUF_SIZE];//包体MsgType mSendType;unsigned short mSendHeader;//包头char mSendPacket[BUF_SIZE];//包体};//接收客户端链接线程void eventSelectThreadCmd(){while (!mTerminated){DWORD dwIndex = WSAWaitForMultipleEvents(mTotalEvent, mAllEvent, false, 100, false);if (WSA_WAIT_FAILED == dwIndex){printf_s("server WSAWaitForMultipleEvents failed!!!\n");continue;}if (WSA_WAIT_TIMEOUT == dwIndex){Sleep(100);continue;}WSANETWORKEVENTS networkEvents;if (SOCKET_ERROR == WSAEnumNetworkEvents(mAllSocket[dwIndex - WSA_WAIT_EVENT_0], mAllEvent[dwIndex - WSA_WAIT_EVENT_0], &networkEvents)){printf_s("server WSAEnumNetworkEvents failed!!!\n");continue;}//acceptif (networkEvents.lNetworkEvents & FD_ACCEPT){sockaddr_in addrClient;int addrClientlen = sizeof(addrClient);SOCKET clientSocket = accept(mAllSocket[dwIndex - WSA_WAIT_EVENT_0], (sockaddr FAR*)&addrClient, &addrClientlen);if (SOCKET_ERROR != clientSocket){mAllSocket[mTotalEvent] = clientSocket;mAllEvent[mTotalEvent] = WSACreateEvent();WSAEventSelect(mAllSocket[mTotalEvent], mAllEvent[mTotalEvent], FD_READ | FD_WRITE | FD_CLOSE);mTotalEvent++;Client * client = new Client(clientSocket);mClientSockets.push_back(client);printf_s("accept new client,IP : %sPort : %d\n", inet_ntoa(addrClient.sin_addr), ntohs(addrClient.sin_port));}}//readif (networkEvents.lNetworkEvents & FD_READ){std::list<Client*>::iterator iter = mClientSockets.begin();std::list<Client*>::iterator end = mClientSockets.end();for (; iter != end; ++iter){if ((*iter)->mSocket == mAllSocket[dwIndex - WSA_WAIT_EVENT_0]){(*iter)->handleRecv();}}}//writeif (networkEvents.lNetworkEvents & FD_WRITE){//当收到第一个FD_WRITE时便认为可以send;send失败返回WSAEWOULDBLOCKE时,则需要再一次收到FD_WRITE才能send;//send这里就简单处理为recv后直接send}//closeif (networkEvents.lNetworkEvents & FD_CLOSE){std::list<Client*>::iterator iter = mClientSockets.begin();std::list<Client*>::iterator end = mClientSockets.end();for (; iter != end; ++iter){if ((*iter)->mSocket == mAllSocket[dwIndex - WSA_WAIT_EVENT_0]){delete *iter;mClientSockets.erase(iter);WSACloseEvent(mAllEvent[dwIndex - WSA_WAIT_EVENT_0]);for (int m = dwIndex - WSA_WAIT_EVENT_0;m < mTotalEvent - 1;++m){mAllEvent[m] = mAllEvent[m + 1];mAllSocket[m] = mAllSocket[m + 1];}mTotalEvent--;break;}}}}}int main(){mainThread();return 1;}


Overlapped I/O模型


非阻塞调用,通过windows的重叠I/O机制实现数据复制到用户空间为非阻塞行为。

以完成例程实现
#define _WINSOCK_DEPRECATED_NO_WARNINGS#include <cstdio>#include <iostream>#include <string>#include <algorithm>#include <WinSock2.h>#include <thread>#include <mutex>#include <list>#pragma comment(lib, "WS2_32.lib")#define BUF_SIZE 128bool mTerminated = false;SOCKETmListenSocket;//监听套接字class Client;std::list<Client*> mClientSockets;std::list<Client*> mNewSockets;std::mutex mtx;void InputThreadCmd();void acceptThreadCmd();void HandleThreadCmd();void CleanUpEnvironment(){if (INVALID_SOCKET != mListenSocket){closesocket(mListenSocket);}WSACleanup();}void mainThread(){#pragma region 初始化网络环境//初始化socket动态库WSADATAmWsd;if (0 != WSAStartup(MAKEWORD(2,2), &mWsd)){printf_s("WSAStartup failed!\n");return;}//初始化服务器socketmListenSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);if (INVALID_SOCKET == mListenSocket){printf_s("init server socket failed!\n");CleanUpEnvironment();return;}//设置监听套接字为非阻塞模式u_long ulUnBlocking = 1;int ret = ioctlsocket(mListenSocket, FIONBIO, &ulUnBlocking);if (SOCKET_ERROR == ret){printf("server socket ioctlsocket failed\n");CleanUpEnvironment();return;}//绑定服务器socketSOCKADDR_IN mServerAddr;mServerAddr.sin_family = AF_INET;mServerAddr.sin_port = htons(21234);mServerAddr.sin_addr.s_addr = INADDR_ANY;if (SOCKET_ERROR == bind(mListenSocket, (LPSOCKADDR)&mServerAddr, sizeof(SOCKADDR_IN))){printf_s("server socket bind failed!\n");CleanUpEnvironment();return;}//监听if (SOCKET_ERROR == listen(mListenSocket, SOMAXCONN)){printf_s("server socket listen failed!\n");return;}#pragma endregion#pragma region 开启线程printf_s("server star success!!!\n");std::thread acceptThread(acceptThreadCmd);acceptThread.detach();std::thread handleThread(HandleThreadCmd);handleThread.detach();std::thread inputThread(InputThreadCmd);inputThread.detach();#pragma endregionchar s[128];while (!mTerminated){sprintf_s(s, "OnLine: %d\n", mClientSockets.size());::SetConsoleTitle(s);std::this_thread::sleep_for(std::chrono::milliseconds(1));}}void InputThreadCmd(){while (true){std::string cmdLine;if (getline(std::cin, cmdLine)){if (cmdLine == "//q"){mTerminated = true;}}}}class Client{public:Client(SOCKET s):mSocket(s){ZeroMemory(&mRecvOverLapped,sizeof(mRecvOverLapped));ZeroMemory(&mSendOverLapped, sizeof(mSendOverLapped));}~Client(){closesocket(mSocket);}void RecvHeader(){ZeroMemory(&mRecvOverLapped,sizeof(mRecvOverLapped));mRecvOverLapped.hEvent = WSAEVENT(this);mRecvHeader = 0;WSABUF wsaRecv;wsaRecv.buf = (char*)&mRecvHeader;wsaRecv.len = sizeof(mRecvHeader);DWORDdwBytesRecved;//接收字节数DWORDdwFlags = 0;//标志WSARecv(mSocket, &wsaRecv, 1, &dwBytesRecved, &dwFlags, &mRecvOverLapped, RecvRoutine);}void RecvPacket(){ZeroMemory(&mRecvOverLapped, sizeof(mRecvOverLapped));mRecvOverLapped.hEvent = WSAEVENT(this);ZeroMemory(mRecvPacket, BUF_SIZE);WSABUF wsaRecv;wsaRecv.buf = mRecvPacket;wsaRecv.len = mRecvHeader;DWORDdwBytesRecved;//接收字节数DWORDdwFlags = 0;//标志WSARecv(mSocket, &wsaRecv, 1, &dwBytesRecved, &dwFlags, &mRecvOverLapped, RecvRoutine);}void Send(){DWORDdwBytesRecved;//接收字节数DWORDdwFlags = 0;//标志ZeroMemory(&mSendOverLapped, sizeof(mSendOverLapped));//重叠结构mSendOverLapped.hEvent = WSAEVENT(this);WSABUF wsaSendBuf[2];//WSABUF结构数组//数据包头wsaSendBuf[0].buf = (char*)&mSendHeader;wsaSendBuf[0].len = sizeof(mSendHeader);//数据包体wsaSendBuf[1].buf = mSendPacket;wsaSendBuf[1].len = mSendHeader;WSASend(mSocket, wsaSendBuf, 2, &dwBytesRecved, dwFlags, &mSendOverLapped, SendRoutine);}static void CALLBACK RecvRoutine(DWORD error,DWORD BytesTransferred, LPWSAOVERLAPPED Overlapped, DWORD InFlags){if (0 != error || 0 == BytesTransferred){return;}Client* client = (Client*)Overlapped->hEvent;if (0 != client->mRecvHeader)//接收包体{client->RecvPacket();client->mRecvHeader = 0;}else{int endIndex = 0;for (int m = 0;m < BUF_SIZE;++m){if (client->mRecvPacket[m] == '\0'){endIndex = m;break;}}client->mSendHeader = endIndex;ZeroMemory(client->mSendPacket, BUF_SIZE);std::transform(client->mRecvPacket, client->mRecvPacket + endIndex, client->mSendPacket, towupper);client->Send();}}static void CALLBACK SendRoutine(DWORD error, DWORD BytesTransferred, LPWSAOVERLAPPED Overlapped, DWORD InFlags){if (0 != error || 0 == BytesTransferred){return;}Client* client = (Client*)Overlapped->hEvent;printf_s("send success : %s\n", client->mSendPacket);client->RecvHeader();}SOCKET mSocket;unsigned short mRecvHeader;//包头char mRecvPacket[BUF_SIZE];//包体unsigned short mSendHeader;//包头char mSendPacket[BUF_SIZE];//包体WSAOVERLAPPED mRecvOverLapped;WSAOVERLAPPED mSendOverLapped;};//接收客户端链接线程void acceptThreadCmd(){printf_s("server begin accept client...\n");sockaddr_in addrClient;int addrClientlen = sizeof(addrClient);while (!mTerminated){SOCKET clientSocket = accept(mListenSocket, (sockaddr FAR*)&addrClient, &addrClientlen);if (SOCKET_ERROR != clientSocket){Client * client = new Client(clientSocket);mClientSockets.push_back(client);mtx.lock();mNewSockets.push_back(client);mtx.unlock();printf_s("accept new client,IP : %sPort : %d\n", inet_ntoa(addrClient.sin_addr), ntohs(addrClient.sin_port));}SleepEx(100, true);}while (mClientSockets.size() > 0){delete mClientSockets.front();mClientSockets.pop_front();}}//收发线程void HandleThreadCmd(){while (!mTerminated){mtx.lock();while (mNewSockets.size() > 0){mNewSockets.front()->RecvHeader();mNewSockets.pop_front();}mtx.unlock();SleepEx(100, true);}}int main(){mainThread();return 1;}
以事件实现
#define _WINSOCK_DEPRECATED_NO_WARNINGS#include <cstdio>#include <iostream>#include <string>#include <algorithm>#include <WinSock2.h>#include <thread>#include <list>#include <map>#pragma comment(lib, "WS2_32.lib")#define BUF_SIZE 128bool mTerminated = false;SOCKETmListenSocket;//监听套接字class Client;std::list<Client*> mClientSockets;std::map<WSAEVENT, WSAOVERLAPPED*> mAllClientOverlappedEvent;std::map<WSAEVENT, Client*> mEventSockets;int mEventIndex = 0;WSAEVENT mAllEvent[WSA_MAXIMUM_WAIT_EVENTS];void InputThreadCmd();void acceptThreadCmd();void HandleThreadCmd();void CleanUpEnvironment(){if (INVALID_SOCKET != mListenSocket){closesocket(mListenSocket);}WSACleanup();}void mainThread(){#pragma region 初始化网络环境//初始化socket动态库WSADATAmWsd;if (0 != WSAStartup(MAKEWORD(2, 2), &mWsd)){printf_s("WSAStartup failed!\n");return;}//初始化服务器socketmListenSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);if (INVALID_SOCKET == mListenSocket){printf_s("init server socket failed!\n");CleanUpEnvironment();return;}//设置监听套接字为非阻塞模式u_long ulUnBlocking = 1;int ret = ioctlsocket(mListenSocket, FIONBIO, &ulUnBlocking);if (SOCKET_ERROR == ret){printf("server socket ioctlsocket failed\n");CleanUpEnvironment();return;}//绑定服务器socketSOCKADDR_IN mServerAddr;mServerAddr.sin_family = AF_INET;mServerAddr.sin_port = htons(21234);mServerAddr.sin_addr.s_addr = INADDR_ANY;if (SOCKET_ERROR == bind(mListenSocket, (LPSOCKADDR)&mServerAddr, sizeof(SOCKADDR_IN))){printf_s("server socket bind failed!\n");CleanUpEnvironment();return;}//监听if (SOCKET_ERROR == listen(mListenSocket, SOMAXCONN)){printf_s("server socket listen failed!\n");return;}#pragma endregion#pragma region 开启线程printf_s("server star success!!!\n");for (int m = 0;m < WSA_MAXIMUM_WAIT_EVENTS;++m){mAllEvent[m] = NULL;}std::thread acceptThread(acceptThreadCmd);acceptThread.detach();std::thread handleThread(HandleThreadCmd);handleThread.detach();std::thread inputThread(InputThreadCmd);inputThread.detach();#pragma endregionchar s[128];while (!mTerminated){sprintf_s(s, "OnLine: %d\n", mClientSockets.size());::SetConsoleTitle(s);std::this_thread::sleep_for(std::chrono::milliseconds(1));}}void InputThreadCmd(){while (true){std::string cmdLine;if (getline(std::cin, cmdLine)){if (cmdLine == "//q"){mTerminated = true;}}}}class Client{public:Client(SOCKET s):mSocket(s){ZeroMemory(&mRecvOverLapped, sizeof(mRecvOverLapped));mRecvEvent = WSACreateEvent();mRecvOverLapped.hEvent = mRecvEvent;ZeroMemory(&mSendOverLapped, sizeof(mSendOverLapped));mSendEvent = WSACreateEvent();mSendOverLapped.hEvent = mSendEvent;}~Client(){closesocket(mSocket);}void RecvHeader(){mRecvHeader = 0;WSABUF wsaRecv;wsaRecv.buf = (char*)&mRecvHeader;wsaRecv.len = sizeof(mRecvHeader);DWORDdwBytesRecved;//接收字节数DWORDdwFlags = 0;//标志WSARecv(mSocket, &wsaRecv, 1, &dwBytesRecved, &dwFlags, &mRecvOverLapped, NULL);}void RecvPacket(){ZeroMemory(mRecvPacket, BUF_SIZE);WSABUF wsaRecv;wsaRecv.buf = mRecvPacket;wsaRecv.len = mRecvHeader;DWORDdwBytesRecved;//接收字节数DWORDdwFlags = 0;//标志WSARecv(mSocket, &wsaRecv, 1, &dwBytesRecved, &dwFlags, &mRecvOverLapped, NULL);}void Send(){DWORDdwBytesRecved;//接收字节数DWORDdwFlags = 0;//标志WSABUF wsaSendBuf[2];//WSABUF结构数组//数据包头wsaSendBuf[0].buf = (char*)&mSendHeader;wsaSendBuf[0].len = sizeof(mSendHeader);//数据包体wsaSendBuf[1].buf = mSendPacket;wsaSendBuf[1].len = mSendHeader;WSASend(mSocket, wsaSendBuf, 2, &dwBytesRecved, dwFlags, &mSendOverLapped, NULL);}void RecvHandleEvent(){if (0 != mRecvHeader)//接收包体{RecvPacket();mRecvHeader = 0;}else{int endIndex = 0;for (int m = 0;m < BUF_SIZE;++m){if (mRecvPacket[m] == '\0'){endIndex = m;break;}}mSendHeader = endIndex;ZeroMemory(mSendPacket, BUF_SIZE);std::transform(mRecvPacket, mRecvPacket + endIndex, mSendPacket, towupper);Send();}}void SendHandleEvent(){printf_s("send success : %s\n", mSendPacket);RecvHeader();}SOCKET mSocket;unsigned short mRecvHeader;//包头char mRecvPacket[BUF_SIZE];//包体unsigned short mSendHeader;//包头char mSendPacket[BUF_SIZE];//包体WSAOVERLAPPED mRecvOverLapped;WSAEVENT mRecvEvent;WSAOVERLAPPED mSendOverLapped;WSAEVENT mSendEvent;};//接收客户端链接线程void acceptThreadCmd(){printf_s("server begin accept client...\n");sockaddr_in addrClient;int addrClientlen = sizeof(addrClient);while (!mTerminated){SOCKET clientSocket = accept(mListenSocket, (sockaddr FAR*)&addrClient, &addrClientlen);if (SOCKET_ERROR != clientSocket){Client * client = new Client(clientSocket);mClientSockets.push_back(client);mEventSockets[client->mRecvEvent] = client;mEventSockets[client->mSendEvent] = client;mAllEvent[mEventIndex++] = client->mRecvEvent;mAllClientOverlappedEvent[client->mRecvEvent] = &client->mRecvOverLapped;mAllEvent[mEventIndex++] = client->mSendEvent;mAllClientOverlappedEvent[client->mSendEvent] = &client->mSendOverLapped;printf_s("accept new client,IP : %sPort : %d\n", inet_ntoa(addrClient.sin_addr), ntohs(addrClient.sin_port));client->RecvHeader();}SleepEx(100, true);}while (mClientSockets.size() > 0){delete mClientSockets.front();mClientSockets.pop_front();}}//收发线程void HandleThreadCmd(){DWORD dwIndex;//返回值DWORD dwFlags;//标志DWORD dwBytesTraned;//实际传输的数据while (!mTerminated){if(0 == mEventIndex)continue;//等待事件if ((dwIndex = WSAWaitForMultipleEvents(mEventIndex, mAllEvent, FALSE, 100, TRUE)) == WSA_WAIT_FAILED){printf("WSAWaitForMultipleEvents 失败 %d\n", WSAGetLastError());continue;}if (WSA_WAIT_TIMEOUT == dwIndex){continue;}WSAEVENT handleEvent = mAllEvent[dwIndex - WSA_WAIT_EVENT_0];Client* client = mEventSockets[handleEvent];//重置事件WSAResetEvent(handleEvent);WSAOVERLAPPED* overlapped = mAllClientOverlappedEvent[handleEvent];//检查操作完成状态BOOL bRet = WSAGetOverlappedResult(client->mSocket, overlapped, &dwBytesTraned, TRUE, &dwFlags);if (bRet != FALSE && dwBytesTraned != 0)//发生错误{if (&client->mRecvOverLapped == overlapped){client->RecvHandleEvent();}else if (&client->mSendOverLapped == overlapped){client->SendHandleEvent();}}}}int main(){mainThread();return 1;}


完成端口 I/O模型


完成端口对象的并发线程会等待系统的I/Ocompletion packet,接收后会唤醒服务线程池中上一个执行的线程处理。
理解完成端口需要理解2个线程数量。[1]完成端口并发线程数;[2]服务线程数。这些线程数同CPU核心相关,一般[1]为CPU核心数,[2]为CPU核心数的2倍。那么理论上提高CPU核心就可以直接提高效率。
#define _WINSOCK_DEPRECATED_NO_WARNINGS#include <cstdio>#include <iostream>#include <string>#include <algorithm>#include <WinSock2.h>#include <thread>#include <mutex>#include <list>#pragma comment(lib, "WS2_32.lib")#define BUF_SIZE 128bool mTerminated = false;SOCKETmListenSocket;//监听套接字class Client;std::list<Client*> mClientSockets;HANDLE mIOCompletion;//完成端口void InputThreadCmd();void acceptThreadCmd();void ServeThreadCmd();void CleanUpEnvironment(){if (INVALID_SOCKET != mListenSocket){closesocket(mListenSocket);}WSACleanup();}void mainThread(){#pragma region 初始化网络环境//初始化socket动态库WSADATAmWsd;if (0 != WSAStartup(MAKEWORD(2, 2), &mWsd)){printf_s("WSAStartup failed!\n");return;}//初始化服务器socketmListenSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);if (INVALID_SOCKET == mListenSocket){printf_s("init server socket failed!\n");CleanUpEnvironment();return;}//绑定服务器socketSOCKADDR_IN mServerAddr;mServerAddr.sin_family = AF_INET;mServerAddr.sin_port = htons(21234);mServerAddr.sin_addr.s_addr = INADDR_ANY;if (SOCKET_ERROR == bind(mListenSocket, (LPSOCKADDR)&mServerAddr, sizeof(SOCKADDR_IN))){printf_s("server socket bind failed!\n");CleanUpEnvironment();return;}//监听if (SOCKET_ERROR == listen(mListenSocket, SOMAXCONN)){printf_s("server socket listen failed!\n");CleanUpEnvironment();return;}mIOCompletion = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);if (NULL == mIOCompletion){printf_s("server CreateIoCompletionPort failed!\n");CleanUpEnvironment();return;}#pragma endregion#pragma region 开启线程printf_s("server star success!!!\n");std::thread acceptThread(acceptThreadCmd);acceptThread.detach();std::thread inputThread(InputThreadCmd);inputThread.detach();#pragma endregion#pragma region 完成端口 服务线程SYSTEM_INFO info;GetSystemInfo(&info);for (int m = 0;m < info.dwNumberOfProcessors * 2;++m){std::thread serveThread(ServeThreadCmd);serveThread.detach();}#pragma endregionchar s[128];while (!mTerminated){sprintf_s(s, "OnLine: %d\n", mClientSockets.size());::SetConsoleTitle(s);std::this_thread::sleep_for(std::chrono::milliseconds(1));}}void InputThreadCmd(){while (true){std::string cmdLine;if (getline(std::cin, cmdLine)){if (cmdLine == "//q"){mTerminated = true;}}}}enum MsgType{None,RecvHead,RecvPack,SendData,};struct ClientIOData{OVERLAPPED mRecvOverLapped;MsgType mType;};class Client{public:Client(SOCKET s):mSocket(s){}~Client(){closesocket(mSocket);}void RecvHeader(){ZeroMemory(&mRecvIOData, sizeof(ClientIOData));mRecvIOData.mType = RecvHead;mRecvHeader = 0;WSABUF wsaRecv;wsaRecv.buf = (char*)&mRecvHeader;wsaRecv.len = sizeof(mRecvHeader);DWORDdwBytesRecved;//接收字节数DWORDdwFlags = 0;//标志WSARecv(mSocket, &wsaRecv, 1, &dwBytesRecved, &dwFlags, &mRecvIOData.mRecvOverLapped, NULL);}void RecvPacket(){ZeroMemory(&mRecvIOData, sizeof(ClientIOData));mRecvIOData.mType = RecvPack;ZeroMemory(mRecvPacket, BUF_SIZE);WSABUF wsaRecv;wsaRecv.buf = mRecvPacket;wsaRecv.len = mRecvHeader;DWORDdwBytesRecved;//接收字节数DWORDdwFlags = 0;//标志WSARecv(mSocket, &wsaRecv, 1, &dwBytesRecved, &dwFlags, &mRecvIOData.mRecvOverLapped, NULL);}void Send(){int endIndex = 0;for (int m = 0;m < BUF_SIZE;++m){if (mRecvPacket[m] == '\0'){endIndex = m;break;}}mSendHeader = endIndex;ZeroMemory(mSendPacket, BUF_SIZE);std::transform(mRecvPacket, mRecvPacket + endIndex, mSendPacket, towupper);DWORDdwBytesRecved;//接收字节数DWORDdwFlags = 0;//标志ZeroMemory(&mSendIOData, sizeof(ClientIOData));mSendIOData.mType = SendData;WSABUF wsaSendBuf[2];//WSABUF结构数组//数据包头wsaSendBuf[0].buf = (char*)&mSendHeader;wsaSendBuf[0].len = sizeof(mSendHeader);//数据包体wsaSendBuf[1].buf = mSendPacket;wsaSendBuf[1].len = mSendHeader;WSASend(mSocket, wsaSendBuf, 2, &dwBytesRecved, dwFlags, &mSendIOData.mRecvOverLapped, NULL);}SOCKET mSocket;unsigned short mRecvHeader;//包头char mRecvPacket[BUF_SIZE];//包体unsigned short mSendHeader;//包头char mSendPacket[BUF_SIZE];//包体ClientIOData mRecvIOData;ClientIOData mSendIOData;};//接收客户端链接线程void acceptThreadCmd(){printf_s("server begin accept client...\n");sockaddr_in addrClient;int addrClientlen = sizeof(addrClient);while (!mTerminated){SOCKET clientSocket = accept(mListenSocket, (sockaddr FAR*)&addrClient, &addrClientlen);if (SOCKET_ERROR != clientSocket){Client * client = new Client(clientSocket);//将socket和完成端口关联起来if (NULL == CreateIoCompletionPort((HANDLE)clientSocket, mIOCompletion, (DWORD)client, 0)){continue;}mClientSockets.push_back(client);printf_s("accept new client,IP : %sPort : %d\n", inet_ntoa(addrClient.sin_addr), ntohs(addrClient.sin_port));client->RecvHeader();}SleepEx(100, true);}while (mClientSockets.size() > 0){delete mClientSockets.front();mClientSockets.pop_front();}}void ServeThreadCmd(){DWORD mIOSize;Client*mClient;LPOVERLAPPED mOverlapped;while (!mTerminated){mIOSize = 0;mClient = NULL;mOverlapped = NULL;bool retVal = GetQueuedCompletionStatus(mIOCompletion, &mIOSize, (PULONG_PTR)&mClient, &mOverlapped, INFINITE);if (retVal && NULL != mClient && NULL != mOverlapped && mIOSize > 0){ClientIOData* ioData = CONTAINING_RECORD(mOverlapped, ClientIOData, mRecvOverLapped);if (ioData->mType == RecvHead){mClient->RecvPacket();}else if (ioData->mType == RecvPack){mClient->Send();}else if (ioData->mType == SendData){printf_s("send success : %s\n", mClient->mSendPacket);mClient->RecvHeader();}}}}int main(){mainThread();return 1;}


总结


通过学习Socket网络编程,我认为要开发高效的网络库应该需要注意阻塞,循环调用,线程数量,链接数,IO。这些在接下来学习商业开源网络库的时候需要重点关注。
原创粉丝点击