其实已经有很多大佬将原理讲的十分详细了,所以就不花费时间将原理再一次重复讲一遍,有需要的可以自行去查看。
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#include #include 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;}