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的后续文章.今天好不容易有 ...
随机推荐
- fasttext使用笔记
http://blog.csdn.net/m0_37306360/article/details/72832606 这里记录使用fastText训练word vector笔记 github地址:htt ...
- Spark:求出分组内的TopN
制作测试数据源: c1 85 c2 77 c3 88 c1 22 c1 66 c3 95 c3 54 c2 91 c2 66 c1 54 c1 65 c2 41 c4 65 spark scala实现 ...
- Cognos11中报XQE-JDB-0004查找驱动程序类错误
1:问题描述,在cognos11中创建了一个数据源连接TestData, 类型为Microsoft SQL Server (OLE DB) 操作如下: 同时配置了jdbc的连接方式,如下图所示 测试数 ...
- [Algorithm] Circular buffer
You run an e-commerce website and want to record the last N order ids in a log. Implement a data str ...
- WIN10系统 截图或者某些程序时屏幕会自动放大怎么办
右击这个应用程序,兼容性,以兼容模式运行,同时勾选高DPI设置时禁止显示缩放即可
- Dockerfile 指令 ADD 和 COPY介绍
一.ADD指令 ADD指令的功能是将主机构建环境(上下文)目录中的文件和目录.以及一个URL标记的文件 拷贝到镜像中. 其格式是: ADD 源路径 目标路径 如: #test FROM ubunt ...
- Go语言类型转换库【github.com/demdxx/gocast】的用法
一.导入库: go get github.com/demdxx/gocast 二.测试代码: // main.go package main import ( "fmt" &quo ...
- Linux内核配置:Makefile目标
在顶层Linux源码目录中输入命令make help,它会显示一长串从源码树中生成的目标列表.最常见的使用make的方式是不指定目标,在这种情况下,它会生成内核ELF文件vmlinux和针对所选架构的 ...
- 结合JSFL/actionscript 实现轮廓动画
动画前半段通过JSFL获取轮廓数据,并在EnterFrame中逐个边缘画出的:后半段机枪动画是美术做好的flash动画. 这里只放出actionscript代码,而JSFL代码涉及到一个工程,暂时保密 ...
- django之异常错误3(Student matching query does not exist.)
错误提示: DoesNotExist at /blog/test2/ Student matching query does not exist. 说明:错误提示说明错误在test2中,查找数据库的表 ...