其实已经有很多大佬将原理讲的十分详细了,所以就不花费时间将原理再一次重复讲一遍,有需要的可以自行去查看。
http://blog.csdn.net/beyond_cn/article/details/9336043 这篇文章是我看的,原理介绍十分详细。不过有一些操作感觉比较复杂因此我简化了许多。还是要感谢大佬们倾力普及知识
IOCP模型的关键呢就是将完成端口与套接字绑定起来,然后在这个套接字上投递一个接收请求。然后工作线程得到通知,从缓冲区中取出数据(完成端口已经帮我们将数据从套接字中取到缓冲区,要知道这一步是挺耗时的,所以节省了挺多时间)。我在实现的时候就会有疑问:工作线程仅仅取到数据就好吗?因为我肯定得知道是从哪个套接字中取到的,以便接下来可以接着投递接收请求以及发送请求。那么关键就是在于你绑定完成端口与套接字函数CreateIoCompletionPort()的第三个参数。这个参数我传进去的是一个指针,指针指向的结构体包含了套接字、缓冲区等等内容(这意味着你的这个结构体需要放在堆里面能够被多个线程访问到,这里我用GlobalAlloc)。然后在工作线程使用GetQueuedCompletionStatus()从第三个参数中就能获得你传进去的那个指针的地址,你就可以通过这个地址获得指针进而访问到传进去的结构体。当然也有比较简单的办法就是利用全局变量,然后你在CreateIoCompletionPort()传入的是i(标识符),标明这个是第几个。然后从对应的一系列数组获得你想要的信息。最好的话还是建一个结构体,把需要的东西都打包在一起。
要正常工作起来的话需要注意WSARecv中传入的wsabuf需要初始化好,包括其buf和len。我就是因为len没初始化只初始化了buf导致之前一直失败。
//IOCP代码 #pragma comment(lib, "ws2_32.lib") #include <WinSock2.h> #include <stdio.h> #include <iostream> using namespace std; #define DATA_BUFSIZE 1024SOCKET ListenSocket;struct PerIOcontext {OVERLAPPED m_Overlapped;SOCKET m_sockAccept;WSABUF m_wsaBuf;char buffer[DATA_BUFSIZE]; };DWORD WINAPI AcceptThread(LPVOID lpParameter) {WSADATA wsaData;WSAStartup(MAKEWORD(2,2), &wsaData);ListenSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, NULL, WSA_FLAG_OVERLAPPED);SOCKADDR_IN ServerAddr;ServerAddr.sin_family = AF_INET;ServerAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);ServerAddr.sin_port = htons(1234);bind(ListenSocket, (LPSOCKADDR)&ServerAddr, sizeof(ServerAddr));listen(ListenSocket, 100);printf("listenning...\n");int i = 0;SOCKADDR_IN ClientAddr;int addr_length = sizeof(ClientAddr);HANDLE completionPort = (HANDLE)lpParameter;while(TRUE){PerIOcontext* perIOcontext = (PerIOcontext*)GlobalAlloc(GPTR, sizeof(PerIOcontext));SOCKET acceptSocket;SOCKADDR_IN acceptAddr;int len = sizeof(acceptAddr);acceptSocket = accept(ListenSocket, (SOCKADDR*)&acceptAddr, &len);printf("接受到客户端连接");if(SOCKET_ERROR == perIOcontext->m_sockAccept){ // 接收客户端失败 cerr << "Accept Socket Error: " << GetLastError() << endl; system("pause"); return -1; } perIOcontext->m_wsaBuf.buf = perIOcontext->buffer;perIOcontext->m_wsaBuf.len = 1024;perIOcontext->m_sockAccept = acceptSocket;CreateIoCompletionPort((HANDLE)(perIOcontext->m_sockAccept), completionPort, (DWORD)perIOcontext, 0);DWORD RecvBytes; DWORD Flags = 0;ZeroMemory(&(perIOcontext->m_Overlapped), sizeof(OVERLAPPED));WSARecv(perIOcontext->m_sockAccept, &(perIOcontext->m_wsaBuf), 1, &RecvBytes, &Flags, &(perIOcontext->m_Overlapped), NULL);}return FALSE; }DWORD WINAPI ReceiveThread(LPVOID lpParameter) {HANDLE completionPort = (HANDLE)lpParameter;DWORD BytesTransferred;PerIOcontext* perIOcontext;LPOVERLAPPED IpOverlapped = NULL;while(true){printf("Receive线程进入等待\n");BOOL ret = GetQueuedCompletionStatus(completionPort, &BytesTransferred, (PULONG_PTR)&perIOcontext, &IpOverlapped, INFINITE);printf("Receive线程退出等待\n");if (BytesTransferred == 0){printf("获得字节为0,disconnect\n");}printf("客户端:%s\n", perIOcontext->buffer);memset(perIOcontext->buffer, 0, DATA_BUFSIZE);DWORD RecvBytes; DWORD Flags = 0;system("pause");WSARecv(perIOcontext->m_sockAccept, &(perIOcontext->m_wsaBuf), 1, &RecvBytes, &Flags, &(perIOcontext->m_Overlapped), NULL);}return FALSE; }int main() {HANDLE completionPort = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, 0); if (NULL == completionPort){ // 创建IO内核对象失败 cout << "CreateIoCompletionPort failed. Error:" << GetLastError() << endl; system("pause"); return 0; }HANDLE hThreads[2];hThreads[0] = CreateThread(NULL, 0, AcceptThread, completionPort, NULL, NULL);hThreads[1] = CreateThread(NULL, 0, ReceiveThread, completionPort, NULL, NULL);WaitForMultipleObjects(2, hThreads, TRUE, INFINITE);printf("exit\n");CloseHandle(hThreads[0]);CloseHandle(hThreads[1]);return 0; }