其實已經有很多大佬將原理講的十分詳細了,所以就不花費時間將原理再一次重復講一遍,有需要的可以自行去查看。
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 1024 SOCKET 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; }