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的后续文章.今天好不容易有 ...
随机推荐
- Mysql写入中文出错
本地调试好像正常,服务器运行报错: UnicodeEncodeError: 'latin-1' codec can't encode character u'\u5206' in position 2 ...
- border-color: transparent rgb(255, 48, 48) transparent transparent;
border-color: transparent rgb(255, 48, 48) transparent transparent;
- [Functional Programming] Arrow Functor with contramap
What is Arrow Functor? Arrow is a Profunctor that lifts a function of type a -> b and allows for ...
- innerWidth outerWidth
在jQuery中: 一.width()方法用于获得元素宽度: 二.innerWidth()方法用于获得包括内边界(padding)的元素宽度; 三.outerWidth()方法用于获得包括内边界(pa ...
- OpenGL ES 3.0之Uniform详解
Uniform是变量类型的一种修饰符,是OpenGL ES 中被着色器中的常量值,使用存储各种着色器需要的数据,例如:转换矩阵.光照参数或者颜色. uniform 的空间被顶点着色器和片段着色器分享 ...
- 带你走进EJB--将EJB发布为Webservice(4)
接下来的我们将会自定义一个对象,然后看看EJB是如何对复杂的参数发布成WebService的. 代码如下:在第一个版本的基础之上加上增加用户的方法,参数为User. package com.tgb.e ...
- 深入理解JVM内存区域与内存分配
前言:这是一篇关于JVM内存区域的文章,由网上一些有关这方面的文章和<深入理解Java虚拟机>整理而来,所以会有些类同的地方,也不能保证我自己写的比其他网上的和书本上的要好,也不可能会这样 ...
- django之 文件上传功能(缺陷:无法改存放目录)
百度云盘:django之 文件上传功能(缺陷:无法改存放目录)
- Servlet路径跳转问题
Servlet中路径跳转(服务器端跳转)JSP 1.相对路径 注意这里的相对含义,相对于谁而言 经过多次试验总结,servlet相对路径跳转相对于servlet配置的xml路径(或servlet3. ...
- DLib库Base64编解码示例
代码 #include <iostream> #include <fstream> #include <sstream> #include <string&g ...