本文主要是介绍网络编程对象socke中的IOCP完成端口,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
为什么要采用Socket模型,而不直接使用Socket?
原因源于recv()方法是堵塞式的,当多个客户端连接服务器时,其中一个socket的recv调用时,会产生堵塞,使其他链接不能继续。这样我们又想到用多线程来实现,每个socket链接使用一个线程,这样效率十分低下,根本不可能应对负荷较大的情况。于是便有了各种模型的解决方法,总之都是为了实现多个线程同时访问时不产生堵塞。
完成端口(IOCP)模型:
首先来说为什么要使用完成端口:原因还是因为为了解决recv方法为阻塞式的问题,WinSocket封装的WSARecv方法为非堵塞的方法。
int WSARecv(
SOCKET s,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesRecvd,
LPDWORD lpFlags,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
WSARecv为非阻塞的方法,其中第二个参数是I/O请求成功时,数据保存的地址。
Socket的触发是属于网卡硬件的中断信号,只是此信号CPU不能直接获取状态,此时我们可以使之绑定Event事件,Event内核对象的状态时可以监听到的。这也就是WSAEventSelect模型的原理,当然重叠模型的最终原理也是如此。但Event的方法有着其弊病:当模型处理多线程事件时要调用WSAWaitForMultipleEvents函数,WSAWaitForMultipleEvents函数一次最多只能等待64个事件对象。所以当海量客户端连接服务器时,服务器将没有能力应对,于是我们使用完成端口。
完成端口:
HANDLE CreateIoCompletionPort(
HANDLE FileHandle, //要链接的Socket
HANDLE ExistingCompletionPort, //全局完成端口
//同完成端口关联到一起的句柄,此处可为链接的socket,或是id等等(目地使接收到的socket知道是哪个socket)
DWORD CompletionKey,
DWORD NumberOfConcurrentThreads
);
此函数创建创建Socket与完成端口的链接,CreateIoCompletionPort函数被用于完成两个工作:
- 用于创建—个完成端口对象。
- 将一个句柄同完成端口关联到一起。
用函数GetQueuedCompletionStatus等待全局完成端口的完成队列:
BOOL GetQueuedCompletionStatus(
HANDLE CompletionPort,
LPDWORD lpNumberOfBytes,
PULONG_PTR lpCompletionKey, //此参数为CreateIoCompletionPort第三个参数传过来的句柄,通过此参数获得socket
LPOVERLAPPED* lpOverlapped,
DWORD dwMilliseconds
);
完成端口的工作原理是,把Socket和完成端口绑定,通过关联句柄传递传递参数,使得获取到的Socket能得知是那个socket,参数可以自定义可以是socket本身也可以是id等等。
#include "WinSock2.h" #pragma comment(lib, "ws2_32.lib")#define MESSAGESIZE 1024SOCKET serverSocket; DWORD WINAPI SocketProcAccept(LPVOID pParam); DWORD WINAPI SocketProcMain(LPVOID pParam);enum SOCKETOPERATE {soREVC };struct SOCKETDATA {WSAOVERLAPPED overlapped;WSABUF buf;char sMessage[MESSAGESIZE];DWORD dwBytes;DWORD flag;SOCKETOPERATE socketType;void Clear(SOCKETOPERATE type){ZeroMemory(this, sizeof(SOCKETDATA));buf.buf = sMessage;buf.len = MESSAGESIZE;socketType = type;} };SOCKET CreateServiceSocket(int Port) {int iError;WSAData data;iError = WSAStartup(0x0202, &data);SOCKET tmp = socket(AF_INET,SOCK_STREAM,0);if(tmp == INVALID_SOCKET){return INVALID_SOCKET;}SOCKADDR_IN addr;addr.sin_addr.s_addr = inet_addr("127.0.0.1");addr.sin_family = AF_INET;addr.sin_port = htons(Port);if((bind(tmp, (sockaddr*)&addr, sizeof(addr))) != 0){closesocket(tmp);return INVALID_SOCKET;}if((listen(tmp, INFINITE)) != 0){closesocket(tmp);return INVALID_SOCKET;}return tmp; }int _tmain(int argc, _TCHAR* argv[]) {HANDLE CP = INVALID_HANDLE_VALUE;CP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);SYSTEM_INFO systemInfo;GetSystemInfo(&systemInfo);for (int i = 0; i<systemInfo.dwNumberOfProcessors; i++){CreateThread(NULL, NULL, &SocketProcMain, CP, NULL, NULL);}serverSocket = CreateServiceSocket(6565);if (serverSocket == INVALID_SOCKET){return 0;}CreateThread(NULL, NULL, &SocketProcAccept, CP, NULL, NULL);while(1){Sleep(10000);}CloseHandle(CP);closesocket(serverSocket);WSACleanup();return 0; }DWORD WINAPI SocketProcAccept(LPVOID pParam) {HANDLE CP = (HANDLE)pParam;SOCKADDR_IN addr;int len = sizeof(SOCKADDR_IN);SOCKET tmp;SOCKETDATA *lpSocketData;while(1){tmp = accept(serverSocket, (sockaddr*)&addr, &len);printf("Client Accept:%s\t:%d\n", inet_ntoa(addr.sin_addr), htons(addr.sin_port));CreateIoCompletionPort((HANDLE)tmp, CP, (DWORD)tmp, INFINITE);lpSocketData = (SOCKETDATA *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SOCKETDATA));lpSocketData->Clear(soREVC);WSARecv(tmp, &lpSocketData->buf, 1,&lpSocketData->dwBytes, &lpSocketData->flag, &lpSocketData->overlapped, NULL);} }DWORD WINAPI SocketProcMain(LPVOID pParam) {HANDLE CP = (HANDLE)pParam;SOCKADDR_IN addr;DWORD dwBytes;SOCKETDATA *lpSocketData;SOCKET clientSocket;while(1){GetQueuedCompletionStatus(CP, &dwBytes, (PULONG_PTR)&clientSocket, (LPOVERLAPPED*)&lpSocketData, INFINITE);if(dwBytes == 0xFFFFFFFF){return 0;}if(lpSocketData->socketType == soREVC){if(dwBytes == 0){closesocket(clientSocket);HeapFree(GetProcessHeap(), 0, lpSocketData);}else{lpSocketData->sMessage[dwBytes] = '\0';printf("%x\t:%s\n", (DWORD)clientSocket, lpSocketData->sMessage);lpSocketData->Clear(soREVC);WSARecv(clientSocket, &lpSocketData->buf, 1, &lpSocketData->dwBytes, &lpSocketData->flag, &lpSocketData->overlapped, NULL);}}} }
这篇关于网络编程对象socke中的IOCP完成端口的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!