IOCP完全解析
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
{ SOCKET Socket; SOCKADDR_STORAGE ClientAddr; // 将和这个句柄关联的其他有用信息,尽管放在这里面吧 }PER_HANDLE_DATA, *LPPER_HANDLE_DATA; // 但 I/O 操作数据 typedef struct tagPER_IO_DATA { OVERLAPPED Overlapped; WSABUF DataBuf; char buffer[1024]; int BufferLen; int OperationType; // 可以作为读写的标志,为简单,我忽略了 }PER_IO_DATA, *LPPER_IO_DATA; DWORD WINAPI ServerWorkerThread(LPVOID lpParam); /**////////////////////////////////////////////////////////////////////////////// // The one and only application object CWinApp theApp; using namespace std; int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { int nRetCode = 0; // initialize MFC and print来自百度文库and error on failure if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) { // TODO: change error code to suit your needs cerr << _T("Fatal Error: MFC initialization failed") << endl; nRetCode = 1; } else { // TODO: code your application's behavior here. CString strHello; strHello.LoadString(IDS_HELLO); cout << (LPCTSTR)strHello << endl; }
/**/////////////////////////////////////////////////////////////////////////// HANDLE CompletionPort; WSADATA wsd; SYSTEM_INFO SystemInfo; SOCKADDR_IN InternetAddr; SOCKET Listen; // 加载 WinSock2.2 WSAStartup(MAKEWORD(2, 2), &wsd); // 1.创建一个 I/O 完成端口 CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); // 2.确定系统中有多少个处理器 GetSystemInfo(&SystemInfo); // 3.基于系统中可用的处理器数量创建工作器线程 for (int i = 0; i < SystemInfo.dwNumberOfProcessors * 2; ++i) { HANDLE ThreadHandle; // 创建一个服务器的工作器线程,并将完成端口传递到该线程 ThreadHandle = CreateThread(NULL, 0, ServerWorkerThread, CompletionPort, 0, NULL); CloseHandle(ThreadHandle); } // 4.创建一个监听套接字,以下的套路都是固定的。 Listen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
#pragma comment(lib,"ws2_32.lib") 从本质上说,完成端口模型要求创建一个 windows 完成端口对象,该对象通过指定数量的线程,对重叠 I/O 请求进 行管理,以便为已经完成的重叠 I/O 请求提供服务。 首先要创建一个 I/O 完成端口对象,用它面向任意数量的套接字句柄,管理多个 I/O 请求。调用以下函数创建 完成端口对象: HANDLE CreateIoCompletionPort( HANDLE FileHandle,// 同 IOCP 关联在一起的套接字句柄 HANDLE ExistingCompletionPort,// IOCP 句柄 ULONG_PTR CompletionKey, // 完成健 DWORD NumberOfConcurrentThreads // 在 IOCP 上,同时允许执行的线程数量 ); 该函数有两个作用: (1)创建一个完成端口对象 (2)将一个句柄同完成端口关联到一起 然后就要创建一定数量的工作者线程,以便在套接字的 I/O 请求投递给完成端口后,为完成端口提供服务。写 文字描述很烦,还是看代码吧: // NetServer3.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "NetServer3.h" #include <winsock2.h> #pragma comment(lib, "ws2_32.lib") #include <iostream> using namespace std; /**/////////////////////////////////////////////////////////////////////////// #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif /**/////////////////////////////////////////////////////////////////////////// // 单句柄数据 typedef struct tagPER_HANDLE_DATA
InternetAddr.sin_family = PF_INET; InternetAddr.sin_port = htons(5000); InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY); bind(Listen, (SOCKADDR*)&InternetAddr, sizeof(InternetAddr)); listen(Listen, 5); BOOL b = TRUE; while (b) { PER_HANDLE_DATA * PerHandleData = NULL; SOCKADDR_IN saRemote; SOCKET Accept; int RemoteLen; // 5.接收连接,并分配完成端口,这儿可以用 AcceptEx 来代替,以创 // 建可伸缩的 Winsock 应用程序。 RemoteLen = sizeof(saRemote); Accept = accept(Listen, (SOCKADDR*)&saRemote, &RemoteLen); // 6.创建用来和套接字关联的单句柄数据信息结构 PerHandleData = (LPPER_HANDLE_DATA)GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA)); cout << "Socket number " << Accept << " connected" << endl; PerHandleData->Socket = Accept; memcpy(&PerHandleData->ClientAddr, &saRemote, RemoteLen); // 7.将接受套接字和完成端口关联起来 CreateIoCompletionPort((HANDLE)Accept, CompletionPort, (DWORD)PerHandleData, 0); // 开始在接受套接字上处理 I/O // 使用重叠 I/O 机制,在新建的套接字上投递一个或多个异步 // WSARecv 或 WSASend 请求。这些 I/O 请求完成后,工作者线程 // 会为 I/O 请求提供服务,之后就可以坐享其成了 static int const DATA_BUFSIZE = 4096; // DWORD RecvBytes = 0;
(LPOVERLAPPED*) &lpOverlapped, INFINITE); // 检查成功的返回,这儿要注意使用这个宏 CONTAINING_RECORD PerIoData = (LPPER_IO_DATA)CONTAINING_RECORD(lpOverlapped, PER_IO_DATA, Overlapped); // 先检查一下,看看是否在套接字上已有错误发生 if (0 == BytesTransferred) { closesocket(PerHandleData->Socket); GlobalFree(PerHandleData); GlobalFree(PerIoData); continue; } // 数据处理 // 成功了! ! !这儿就收到了来自客户端的数据 cout << PerIoData->DataBuf.buf << endl; Flags = 0; // 为下一个重叠调用建立单 I/O 操作数据 ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED)); PerIoData->DataBuf.len = 1024; PerIoData->DataBuf.buf = PerIoData->buffer; PerIoData->OperationType = 0; // read WSARecv(PerHandleData->Socket, &(PerIoData->DataBuf), 1, &RecvBytes, &Flags, &(PerIoData->Overlapped), NULL); } return 0; }
DWORD Flags = 0; // 单 I/O 操作数据 LPPER_IO_DATA PerIoData = NULL; PerIoData = (LPPER_IO_DATA)GlobalAlloc(GPTR, sizeof(PER_IO_DATA)); ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED)); PerIoData->DataBuf.len = 1024; PerIoData->DataBuf.buf = PerIoData->buffer; PerIoData->OperationType = 0; // read WSARecv(PerHandleData->Socket, &(PerIoData->DataBuf), 1, &RecvBytes, &Flags, &(PerIoData->Overlapped), NULL); } /**/////////////////////////////////////////////////////////////////////////// return nRetCode; } /**/////////////////////////////////////////////////////////////////////////// DWORD WINAPI ServerWorkerThread(LPVOID lpParam) { HANDLE CompletionPort = (HANDLE)lpParam; DWORD BytesTransferred; LPOVERLAPPED lpOverlapped; LPPER_HANDLE_DATA PerHandleData = NULL; LPPER_IO_DATA PerIoData = NULL; DWORD SendBytes; DWORD RecvBytes; DWORD Flags; BOOL bRet = FALSE; while (TRUE) { bRet = GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (PULONG_PTR) &PerHandleData,