用了一个工作线程,可管理64个TCP连接。 // OverlappedIO.cpp : Defines the entry point for the console application. // #include "stdafx.h" using std::map; #define DATA_BUFSIZE 4096 DWORD g_udwIndexEvent = 0; CRITICAL_SECTION g_CSListClients; HANDLE g_hThreadServer; HANDLE g_hEventConn; typedef struct MapClientNode { SOCKET sock; WSAEVENT tEvent; DWORD udwRecvBytes; WSABUF tBuf; WSAOVERLAPPED tOverLp; bool bHasHandled; }_CLIENT; typedef map<WSAEVENT, _CLIENT> MAP_CLIENT; MAP_CLIENT g_mapClient; #define _ENTER_CS_CLT EnterCriticalSection(&g_CSListClients) #define _LEAVE_CS_CLT LeaveCriticalSection(&g_CSListClients) int WriteToArray(MAP_CLIENT::iterator begin, MAP_CLIENT::iterator end, WSAEVENT *aEvents) { int c = 0; for (; begin != end; ++begin) { if (false == begin->second.bHasHandled) { aEvents[c++] = begin->second.tEvent; } } return c; } //工作线程 unsigned int __stdcall ServerProc(void *pvParam) { DWORD udwFlags = 0; MAP_CLIENT::iterator iter; WSAEVENT aEvents[WSA_MAXIMUM_WAIT_EVENTS]; for (;;) { WaitForSingleObject(g_hEventConn, INFINITE); _ENTER_CS_CLT; //这个是主要部分,对每个需要投递WSARecv的连接投递一次 for (iter = g_mapClient.begin(); iter != g_mapClient.end();) { if (iter->second.bHasHandled && (WSARecv(iter->second.sock, &iter->second.tBuf, 1, &iter->second.udwRecvBytes, &udwFlags, &iter->second.tOverLp, NULL) == SOCKET_ERROR)) { iter->second.bHasHandled = false; switch (WSAGetLastError()) { case WSAENOBUFS: ++iter; break; case WSA_IO_PENDING: ++iter; break; default: closesocket(iter->second.sock); WSACloseEvent(iter->second.tEvent); g_mapClient.erase(iter++); break; } } else { ++iter; } } //把完成投递但是没被处理的事件写到一个缓冲区中 int c = WriteToArray(g_mapClient.begin(), g_mapClient.end(), aEvents); //等待其中一个的完成,如果超时就返回再重复 DWORD dwIndex = WSAWaitForMultipleEvents(c, aEvents, false, 100, false); if (WSA_WAIT_TIMEOUT == dwIndex) { _LEAVE_CS_CLT; continue; } dwIndex -= WSA_WAIT_EVENT_0; WSAEVENT tEvent = aEvents[dwIndex]; WSAResetEvent(tEvent); //解析事件,读取报文 _CLIENT *ptClient = &g_mapClient[tEvent]; WSAGetOverlappedResult(ptClient->sock, &ptClient->tOverLp, &ptClient->udwRecvBytes, false, &udwFlags); ptClient->bHasHandled = true; if (0 == ptClient->udwRecvBytes) { closesocket(ptClient->sock); WSACloseEvent(tEvent); delete[] ptClient->tBuf.buf; g_mapClient.erase(tEvent); printf("connection closed/n"); if (g_mapClient.empty()) { printf("no connection/n"); ResetEvent(g_hEventConn); } } else { ptClient->tBuf.buf[ptClient->udwRecvBytes] = 0; printf("%s/n", ptClient->tBuf.buf); ZeroMemory(&ptClient->tOverLp, sizeof(WSAOVERLAPPED)); ptClient->tOverLp.hEvent = ptClient->tEvent; } _LEAVE_CS_CLT; } return 0; } int main(int argc, char* argv[]) { //一些初始化工作 InitializeCriticalSection(&g_CSListClients); g_hEventConn = CreateEvent(NULL, true, false, NULL); WSADATA wsaData; WSAStartup(MAKEWORD(2,2),&wsaData); //创建一个TCP套接字,带上WSA_FLAG_OVERLAPPED标志 SOCKET ListenSocket = WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED); //创建用于AcceptEx的缓冲区和事件对象 unsigned char ucBuf[256] = {0}; WSAOVERLAPPED AcceptOverlapped; WSAEVENT hWsaEventAccept = WSACreateEvent(); //bind地址开始监听 SOCKADDR_IN ServerAddr; ServerAddr.sin_family = AF_INET; ServerAddr.sin_addr.S_un.S_addr = INADDR_ANY; ServerAddr.sin_port = htons(1234); bind(ListenSocket,(LPSOCKADDR)&ServerAddr,sizeof(ServerAddr)); listen(ListenSocket,5); //起一个工作线程,用于处理客户端连接 ResetEvent(g_hEventConn); g_hThreadServer = (HANDLE)_beginthreadex(NULL, 0, ServerProc, NULL, 0, NULL); for (;;) { DWORD udwRecv = 0; ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED)); AcceptOverlapped.hEvent = hWsaEventAccept; //创建一个新的套接字用于AcceptEx,因为AcceptEx本身不创建套接字 SOCKET AcceptSocket = WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED); //传递一个overlapped结构,这样就就可以用事件对象来通知 AcceptEx(ListenSocket, AcceptSocket, ucBuf, 0, sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, &udwRecv, &AcceptOverlapped); //等待连接事件的发生,待事件被通知时记得要重置 DWORD udwWaitRet = WSAWaitForMultipleEvents(1, &hWsaEventAccept, false, WSA_INFINITE, false); WSAResetEvent(hWsaEventAccept); int dwLocal, dwRemote; sockaddr_in *ptLocalAddr; sockaddr_in *ptRemoteAddr; //解析缓冲区读取远程地址 GetAcceptExSockaddrs(ucBuf, 0, sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, (sockaddr**)&ptLocalAddr, &dwLocal, (sockaddr**)&ptRemoteAddr, &dwRemote); printf("new connection %s-%u/n", inet_ntoa(ptLocalAddr->sin_addr), ntohs(ptRemoteAddr->sin_port)); _CLIENT tClient; //填写这个新连接的数据结构字段,包括套接字,缓冲区和事件对象 tClient.tEvent = WSACreateEvent(); tClient.sock = AcceptSocket; tClient.tBuf.buf = new char[DATA_BUFSIZE]; tClient.tBuf.len = DATA_BUFSIZE; ZeroMemory(&tClient.tOverLp.hEvent, sizeof(WSAOVERLAPPED)); tClient.tOverLp.hEvent = tClient.tEvent; tClient.bHasHandled = true; _ENTER_CS_CLT; //如果已经有64个连接,那么就报错 if (g_mapClient.size() == 64) { closesocket(AcceptSocket); printf("too much connection/n"); continue; } //插入到工作线程的连接表中 g_mapClient.insert(MAP_CLIENT::value_type(tClient.tEvent, tClient)); if (1 == g_mapClient.size()) { SetEvent(g_hEventConn); } _LEAVE_CS_CLT; } CloseHandle(g_hEventConn); CloseHandle(g_hThreadServer); DeleteCriticalSection(&g_CSListClients); return 0; }