//////server #include <WinSock2.h> #include <windows.h> #include <stdio.h> #include <stdlib.h> #include <process.h> //该例子代码在并发情况下存在问题,需要对clientRecord进行mutex保护才能保证操作的有效性。 #define HOST_PORT 10000 #define MAX_BUFF_SIZE 8192 typedef struct msg_watch_para_struct msg_watch_para_t; struct msg_watch_para_struct { HANDLE cp; }; typedef struct wsa_data_struct wsa_data_t; struct wsa_data_struct { WSAOVERLAPPED Overlapped; char Buffer[MAX_BUFF_SIZE]; WSABUF wsabuf; int nTotalBytes; int nSentBytes; SOCKET SocketAccept; }; typedef struct port_info_struct port_info_t; struct port_info_struct { int client_socket; wsa_data_t *pIOContext; }; void CALLBACK CompletionROUTINE( IN DWORD dwError, IN DWORD cbTransferred, IN LPWSAOVERLAPPED lpOverlapped, IN DWORD dwFlags ) { //仅仅是为了看lpOverlapped的值 wsa_data_t *was_data = (wsa_data_t*)lpOverlapped; } //接收线程 unsigned __stdcall msg_watch_thread(void* p) { msg_watch_para_t *para = (msg_watch_para_t*)p; port_info_t *port_info; char buf[1024 * 11]; LPWSAOVERLAPPED lpOverlapped = NULL; wsa_data_t *was_data; BOOL bSuccess = FALSE; DWORD dwIoSize = 0; while (1) { bSuccess = GetQueuedCompletionStatus(para->cp, &dwIoSize, (PDWORD_PTR)&port_info, &lpOverlapped, INFINITE); if (!bSuccess) { DWORD ecode = GetLastError(); } was_data = (wsa_data_t*)lpOverlapped; { DWORD dwRecvNumBytes = 0; int nRet = 0; DWORD dwFlags = 0; while (TRUE) { nRet = WSARecv(port_info->client_socket, &(port_info->pIOContext->wsabuf), 1, &dwRecvNumBytes, &dwFlags, &(port_info->pIOContext->Overlapped), NULL); if( nRet == SOCKET_ERROR ) { if ((ERROR_IO_PENDING != WSAGetLastError())) //数据读取完成 break; //出现其他错误,关闭端口,干掉session printf("WSARecv() Failed: %d\n", WSAGetLastError()); } } buf[1023] = 0; send(port_info->client_socket, buf, 1024, 0); } } return 0; } int main() { WORD wVersionRequested; WSADATA wsaData; int err; msg_watch_para_t watch_para; wVersionRequested = MAKEWORD( 2, 2 ); err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) { /* Tell the user that we could not find a usable */ /* WinSock DLL. */ return 0; } /* Confirm that the WinSock DLL supports 2.2.*/ /* Note that if the DLL supports versions greater */ /* than 2.2 in addition to 2.2, it will still return */ /* 2.2 in wVersion since that is the version we */ /* requested. */ if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 ) { /* Tell the user that we could not find a usable */ /* WinSock DLL. */ WSACleanup( ); return 0; } int servfd, clientfd; //servfd = socket(AF_INET, SOCK_STREAM, 0); servfd = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_IP, NULL, 0, WSA_FLAG_OVERLAPPED); if(servfd < 0) { printf("server create socket failed.\n"); return 0; } int on = 1; if(setsockopt(servfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)) < 0) { printf("set socket option failed.\n"); return 0; } struct sockaddr_in servaddr; struct sockaddr_in clientaddr; memset(&servaddr,0,sizeof(servaddr)); memset(&clientaddr,0,sizeof(clientaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(HOST_PORT); servaddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY); if(bind(servfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { printf("bind failed.\n"); return 0; } if(listen(servfd,10) < 0) { printf("listen failed.\n"); return 0; } int length = sizeof(clientaddr); watch_para.cp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 10); //创建消息事件处理线程 _beginthreadex((void*)NULL, 1024 * 512, msg_watch_thread, &watch_para, 0, NULL); for(;;) { HANDLE cp; port_info_t *port_info; //接收用户连接 //clientfd = accept(servfd, (struct sockaddr *)&clientaddr,&length); clientfd = WSAAccept(servfd, NULL, NULL, NULL, 0); if ( clientfd == SOCKET_ERROR ) { printf("socket error.\n"); } //初始化端口 port_info = (port_info_t*)malloc(sizeof(port_info_t)); port_info->pIOContext = (wsa_data_t*)malloc(sizeof(wsa_data_t)); { port_info->client_socket = clientfd; port_info->pIOContext->Overlapped.Internal = 0; port_info->pIOContext->Overlapped.InternalHigh = 0; port_info->pIOContext->Overlapped.Offset = 0; port_info->pIOContext->Overlapped.OffsetHigh = 0; port_info->pIOContext->Overlapped.hEvent = NULL; port_info->pIOContext->nTotalBytes = 0; port_info->pIOContext->nSentBytes = 0; port_info->pIOContext->wsabuf.buf = port_info->pIOContext->Buffer; port_info->pIOContext->wsabuf.len = sizeof(port_info->pIOContext->Buffer); } //将端口加入iocp的数组 cp = CreateIoCompletionPort((HANDLE)clientfd, watch_para.cp, (ULONG_PTR)port_info, 0); if (cp == INVALID_HANDLE_VALUE) { DWORD ecode = GetLastError(); } { DWORD dwRecvNumBytes = 0; int nRet = 0; DWORD dwFlags = 0; nRet = WSARecv(clientfd, &(port_info->pIOContext->wsabuf), 1, &dwRecvNumBytes, &dwFlags, &(port_info->pIOContext->Overlapped), NULL); if( nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()) ) { printf("WSARecv() Failed: %d\n", WSAGetLastError()); } } } closesocket(servfd); return 0; }
只是简单的服务器端iocp的代码实现。msg_watch_thread函数完成消息的接收,这个结构设计的目的是使用专门的线程获得消息事件,但本身并不处理消息事件的内容,再次生成任务抛出,由其他的线程进行消息的处理。通过该种方式简化线程工作内容,使不同的线程专注于某个特定领域的工作,每个iocp的对象只需要一个线程进行对应,如果端口较多,并发量一个线程无法承担,则可以增加新的消息监视线程。
不过这种模型下,任务的生成可能成为处理的瓶颈。任务加入队列和从队列中取出将被多个线程争用。不过可以通过将任务的分成多个不同类型的队列,使用多组队列达到并发的效果。