完成端口模型步骤如下:
1.调用CreateIoCompletionPort函数创建完成端口。
HANDLE CompletionPort=CreateIoCompletionStatus(INVALID_HANDLE_VALUE,NULL,0,0);
2.创建和处理器数目相等的工作线程
SYSTEM_INFO SysInfo; GetSystemInfo(&SysInfo); for(int i=0;i<SysInfo.) for(int i=0;i<(sysInfo.dwNumberOfProcessors);i++) { HANDLE ThreadHandle=(HANDLE)_beginthreadex(NULL,0,CompletionPortProcessor,ComplPort,0,NULL); CloseHandle(ThreadHandle); }
3.接受客服端连接请求,创建单句柄数据,调用CreateIoCompletionPort将客服端套接字绑定到完成端口上。
单据句柄数据结构可以自己定义字段:
struct PTR_HANDLE_DATA {//字段可以随意定义 SOCKET s; int i; }
将套接字绑定到完成端口上:
CreateIoCompletionPort(sClient,CompletionPort,(DWORD)PerHandleData,0);
4.创建单I/O数据,并将单I/O数据作为参数传递给重叠I/O函数:WSARecv、WSASend.
创建单I/O数据,该字段除了第一个字段必须为重叠结构OVERLAPPED外,其他字段可以自己定义:
<pre class="cpp" name="code">struct PER_IO_DATA { OVERLAPPED Overlapped; WSABUF DataBuf; char Buffer[DATA_BUFFER]; int OperationType; };
调用重叠I/O函数:
WSARecv(PerHandleData->socket, &PerIoData->DataBuf, 1, &dwRecv, &Flags, &PerIoData->Overlapped, NULL);
5.在工作线程中,调用GetQueuedCompletionStatus函数等待完成端口的完成请求。
GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (LPDWORD)&PerHandleData, (LPOVERLAPPED*)&PerIoData, INFINITE))
6.等待成功后,对请求处理。如果需要再次投递一个重叠I/O。
一个简单的例子如下
#include <WinSock2.h> #include <stdio.h> #include <string.h> #include <ws2tcpip.h> #include <process.h> #pragma comment(lib, "ws2_32.lib ") //linking to the library #define DATA_BUFFER 4*1024 #define RECV_OPERATION 1 #define SEND_OPERATION 2 struct PTR_HANDLE_DATA { SOCKET socket; int Location; }; struct PER_IO_DATA { OVERLAPPED Overlapped; WSABUF DataBuf; char Buffer[DATA_BUFFER]; int OperationType; }; unsigned int WINAPI CompletionPortProcessor(PVOID lParam) { HANDLE CompletionPort = (HANDLE)lParam; DWORD BytesTransferred; PTR_HANDLE_DATA *PerHandleData; PER_IO_DATA *PerIoData; while(true) { if(0 == GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (LPDWORD)&PerHandleData, (LPOVERLAPPED*)&PerIoData, INFINITE)) { if( (GetLastError() == WAIT_TIMEOUT) || (GetLastError() == ERROR_NETNAME_DELETED) ) { closesocket(PerHandleData->socket); delete PerIoData; delete PerHandleData; continue; } return 0; } // 说明客户端已经退出 if(BytesTransferred == 0) { closesocket(PerHandleData->socket); delete PerIoData; delete PerHandleData; continue; } if(PerIoData->OperationType==RECV_OPERATION) { printf("%d:%s\n",PerHandleData->Location,PerIoData->DataBuf.buf); // 继续向 socket 投递WSARecv操作 DWORD Flags = 0; DWORD dwRecv = 0; ZeroMemory(PerIoData, sizeof(PER_IO_DATA)); PerIoData->DataBuf.buf = PerIoData->Buffer; PerIoData->DataBuf.len = DATA_BUFFER; PerIoData->OperationType=RECV_OPERATION; WSARecv(PerHandleData->socket, &PerIoData->DataBuf, 1, &dwRecv, &Flags, &PerIoData->Overlapped, NULL); } } return 0; } void main() { HANDLE ComplPort; ComplPort=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0); SYSTEM_INFO sysInfo; memset(&sysInfo,0,sizeof(sysInfo)); GetSystemInfo(&sysInfo); HANDLE *handleArray=(HANDLE *)malloc(sysInfo.dwNumberOfProcessors *sizeof(HANDLE)); for(int i=0;i<(sysInfo.dwNumberOfProcessors);i++) { handleArray[i]=(HANDLE)_beginthreadex(NULL,0,CompletionPortProcessor,ComplPort,0,NULL); } WSADATA wsaData; WSAStartup(MAKEWORD(2,2),&wsaData); SOCKET s,sClient; struct addrinfo hints,*result; int rc; memset(&hints,0,sizeof(hints)); hints.ai_flags=AI_NUMERICHOST;//nodename is ip address;if set AI_PASSIVE ,it is computer name;if set AI_CANONNAME ,it is .. hints.ai_family=AF_UNSPEC;//IPv4 OR IPv6;if IPv4,Set AF_INET; if IPv6,Set AF_INET6 hints.ai_socktype=SOCK_STREAM;//SOCK_DRGAM hints.ai_protocol=IPPROTO_TCP; rc=getaddrinfo("127.0.0.1","5001",&hints,&result); s=socket(result->ai_family,result->ai_socktype,result->ai_protocol); bind(s,result->ai_addr,result->ai_addrlen); listen(s,5); PER_IO_DATA *PerIoData; PTR_HANDLE_DATA *PerHandleData; int i=1; while(true) { sClient = accept(s, 0,0); PerHandleData = new PTR_HANDLE_DATA(); PerHandleData->socket = sClient; PerHandleData->Location=i; CreateIoCompletionPort((HANDLE)PerHandleData->socket, ComplPort, (DWORD)PerHandleData, 0); PerIoData = new PER_IO_DATA(); ZeroMemory(PerIoData, sizeof(PER_IO_DATA)); PerIoData->DataBuf.buf = PerIoData->Buffer; PerIoData->DataBuf.len =DATA_BUFFER; PerIoData->OperationType=RECV_OPERATION; DWORD Flags = 0; DWORD dwRecv = 0; WSARecv(PerHandleData->socket, &PerIoData->DataBuf, 1, &dwRecv, &Flags, &PerIoData->Overlapped, NULL); i++; } DWORD dwByteTrans; PostQueuedCompletionStatus(ComplPort, dwByteTrans, 0, 0); closesocket(s); }
时间: 2024-11-08 23:47:32