Socket模型(二):完成端口(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 1024 SOCKET 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,);
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))) != )
{
closesocket(tmp);
return INVALID_SOCKET;
} if((listen(tmp, INFINITE)) != )
{
closesocket(tmp);
return INVALID_SOCKET;
} return tmp;
} int _tmain(int argc, _TCHAR* argv[])
{
HANDLE CP = INVALID_HANDLE_VALUE;
CP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, , );
SYSTEM_INFO systemInfo;
GetSystemInfo(&systemInfo);
for (int i = ; i<systemInfo.dwNumberOfProcessors; i++)
{
CreateThread(NULL, NULL, &SocketProcMain, CP, NULL, NULL);
}
serverSocket = CreateServiceSocket();
if (serverSocket == INVALID_SOCKET)
{
return ;
} CreateThread(NULL, NULL, &SocketProcAccept, CP, NULL, NULL); while()
{
Sleep();
}
CloseHandle(CP);
closesocket(serverSocket);
WSACleanup();
return ;
} DWORD WINAPI SocketProcAccept(LPVOID pParam)
{
HANDLE CP = (HANDLE)pParam;
SOCKADDR_IN addr;
int len = sizeof(SOCKADDR_IN);
SOCKET tmp;
SOCKETDATA *lpSocketData;
while()
{
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, ,&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()
{
GetQueuedCompletionStatus(CP, &dwBytes, (PULONG_PTR)&clientSocket, (LPOVERLAPPED*)&lpSocketData, INFINITE);
if(dwBytes == 0xFFFFFFFF)
{
return ;
} if(lpSocketData->socketType == soREVC)
{
if(dwBytes == )
{
closesocket(clientSocket);
HeapFree(GetProcessHeap(), , lpSocketData);
}
else
{
lpSocketData->sMessage[dwBytes] = '\0';
printf("%x\t:%s\n", (DWORD)clientSocket, lpSocketData->sMessage);
lpSocketData->Clear(soREVC);
WSARecv(clientSocket, &lpSocketData->buf, , &lpSocketData->dwBytes, &lpSocketData->flag, &lpSocketData->overlapped, NULL);
}
}
}
}
Socket模型(二):完成端口(IOCP)的更多相关文章
- Socket模型详解(转)
两种I/O模式 一.选择模型 二.异步选择 三.事件选择 四.重叠I/O模型 五.完成端口模型 五种I/O模型的比较 两种I/O模式 1. 两种I/O模式 阻塞模式:执行I/O操作完成前会一直进行等待 ...
- 完成端口IOCP详解
修改自: http://blog.csdn.net/piggyxp/article/details/6922277 ps: 原作者很厉害了, 把一个iocp模型讲解的这么形象,不过在实践过程中发现一些 ...
- 手把手教你玩转SOCKET模型之重叠I/O篇(下)
四. 实现重叠模型的步骤 作 了这么多的准备工作,费了这么多的笔墨,我们终于可以开始着手编码了.其实慢慢的你就会明白,要想透析重叠结构的内部原理也许是要费点功夫,但是只是学会 如何来使用它,却 ...
- .NET平台下几种SOCKET模型的简要性能供参考
转载自:http://www.cnblogs.com/asilas/archive/2006/01/05/311309.html .NET平台下几种SOCKET模型的简要性能供参考 这个内容在cnbl ...
- 转:变手把手教你玩转SOCKET模型之重叠I/O篇
手把手教你玩转SOCKET模型之重叠I/O篇 “身为一个初学者,时常能体味到初学者入门的艰辛,所以总是想抽空作点什么来尽我所能的帮助那些需要帮助的人.我也希望大家能把自己的所学和他人一起分享,不要去鄙 ...
- windows下的IO模型之完成端口
本文整理于:http://blog.csdn.net/piggyxp/article/details/6922277 一. 完成端口的优点 完成端口会充分利用Windows内核来进行I/O的调度,是用 ...
- Socket TCP Server一个端口可以有多少个长连接?受到什么影响?linux最大文件句柄数量总结
Socket TCP Server一个端口可以有多少个长连接? 网上答案很多,不知道那个才是正确的 理论上是无限的 16.Linux中,一个端口能够接受tcp链接数量的理论上限是? A.1024 B. ...
- DELPHI中完成端口(IOCP)的简单分析(4)
DELPHI中完成端口(IOCP)的简单分析(4) 在我以前写的文章中,一直说的是如何接收数据.但是对于如何发送数据却一点也没有提到.因为从代码量上来说接收的代码要比发送多很多.今天我就来写一下如 ...
- DELPHI中完成端口(IOCP)的简单分析(3)
DELPHI中完成端口(IOCP)的简单分析(3) fxh7622关注4人评论7366人阅读2007-01-17 11:18:24 最近太忙,所以没有机会来写IOCP的后续文章.今天好不容易有 ...
随机推荐
- ProBase
http://haixun.olidu.com/probase.html A Data Driven Semantic Network for Text Understanding Probase i ...
- 如何在Centos7上安装和使用ZFS
导读 ZFS文件系统的英文名称为ZettabyteFileSystem,也叫动态文件系统(DynamicFileSystem),是第一个128位文件系统.最初是由Sun公司为Solaris10操作系统 ...
- Cognos11中通过URL传参访问动态Report
一.需求: 在浏览器输入一个URL,在URL后面加上参数就可以访问一个有提示值的报表?比如下面的报表 二.解决办法 Cognos Model 查询主题设计层概要 Select * from [UCO ...
- (算法)AA制
题目: A.B.C.D四个人去吃大餐,吃饭去说好,付钱时AA制,但最后结账时,因为4个人带的钱不一样多,最后A付了112元,B付了86元,C付了10元,D没带钱,所以没有付: 但AA制需要平摊餐费,所 ...
- KafkaOffsetMonitor 安装
KafkaOffsetMonitor 安装 1,下载KafkaOffsetMonitor-assembly-0.2.0.jar 2,启动 步骤1:启动ZK(DN1-DN3节点) zkServer. ...
- zoj 2876 Phone List
#include <stdio.h> #include <string.h> #include <stdlib.h> #define ZERO 0 const in ...
- ubuntu建立快捷方式
拷贝一下文件到新的文件里 [Desktop Entry]Categories=Development;Comment[zh_CN]=Comment=Exec=/usr/local/idea-IU-17 ...
- JAVA设计模式——第 8 章 适配器模式【Adapter Pattern】(转)
好,请安静,后排聊天的同学别吵醒前排睡觉的同学了,大家要相互理解嘛.今天讲适配器模式,这个模式也很简单,你笔记本上的那个拖在外面的黑盒子就是个适配器,一般你在中国能用,在日本也能用,虽然两个国家的的电 ...
- 【转】Window 通过cmd查看端口占用、相应进程、杀死进程等的命令
转自:http://blog.csdn.net/jiangwei0910410003/article/details/18967441 一. 查看所有进程占用的端口 在开始-运行-cmd,输入:ne ...
- Codeforces Round #310 (Div. 1) B. Case of Fugitive(set二分)
B. Case of Fugitive time limit per test 3 seconds memory limit per test 256 megabytes input standard ...