WinSocket模型的探讨——完成端口模型
众所皆知,完成端口是在WINDOWS平台下效率最高,扩展性最好的IO模型,特别针对于WINSOCK的海量连接时,更能显示出其威力。其实建立一个完成端口的服务器也很简单,只要注意几个函数,了解一下关键的步骤也就行了。
这是篇完成端口入门级的文章,分为以下几步来说明完成端口:
- 函数
- 常见问题以及解答
- 步骤
- 例程
1、函数:
我们在完成端口模型下会使用到的最重要的两个函数是:
CreateIoCompletionPort、GetQueuedCompletionStatus
CreateIoCompletionPort 的作用是创建一个完成端口和把一个IO句柄和完成端口关联起来:
// 创建完成端口
HANDLE CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
// 把一个IO句柄和完成端口关联起来,这里的句柄是一个socket 句柄
CreateIoCompletionPort((HANDLE)sClient, CompletionPort, (DWORD)PerHandleData, 0);
其中第一个参数是句柄,可以是文件句柄、SOCKET句柄。
第二个就是我们上面创建出来的完成端口,这里就把两个东西关联在一起了。
第三个参数很关键,叫做PerHandleData,就是对应于每个句柄的数据块。我们可以使用这个参数在后面取到与这个SOCKET对应的数据。
最后一个参数给0,意思就是根据CPU的个数,允许尽可能多的线程并发执行。
GetQueuedCompletionStatus 的作用就是取得完成端口的结果:
// 从完成端口中取得结果
GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (LPDWORD)&PerHandleData, (LPOVERLAPPED*)&PerIoData, INFINITE)
第一个参数是完成端口
第二个参数是表明这次的操作传递了多少个字节的数据
第三个参数是OUT类型的参数,就是前面CreateIoCompletionPort传进去的单句柄数据,这里就是前面的SOCKET句柄以及与之相对应的数据,这里操作系统给我们返回,让我们不用自己去做列表查询等操作了。
第四个参数就是进行IO操作的结果,是我们在投递 WSARecv / WSASend 等操作时传递进去的,这里操作系统做好准备后,给我们返回了。非常省事!!
个人感觉完成端口就是操作系统为我们包装了很多重叠IO的不爽的地方,让我们可以更方便的去使用,下篇我将会尝试去讲述完成端口的原理。
2、常见问题和解答
a、什么是单句柄数据(PerHandle)和单IO数据(PerIO)
单句柄数据就是和句柄对应的数据,像socket句柄,文件句柄这种东西。
单IO数据,就是对应于每次的IO操作的数据。例如每次的WSARecv/WSASend等等
其实我觉得PER是每次的意思,翻译成每个句柄数据和每次IO数据还比较清晰一点。
在完成端口中,单句柄数据直接通过GetQueuedCompletionStatus 返回,省去了我们自己做容器去管理。单IO数据也容许我们自己扩展OVERLAPPED结构,所以,在这里所有与应用逻辑有关的东西都可以在此扩展。
b、如何判断客户端的断开
我们要处理几种情况
1) 如果客户端调用了closesocket,我们就可以这样判断他的断开:
if(0 == GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, 。。。)
{
}
if(BytesTransferred == 0)
{
// 客户端断开,释放资源
}
2) 如果是客户端直接退出,那就会出现64错误,指定的网络名不可再用。这种情况我们也要处理的:
if(0 == GetQueuedCompletionStatus(。。。))
{
if( (GetLastError() == WAIT_TIMEOUT) || (GetLastError() == ERROR_NETNAME_DELETED) )
{
// 客户端断开,释放资源
}
}
3、步骤
编写完成端口服务程序,无非就是以下几个步骤:
1、创建一个完成端口
2、根据CPU个数创建工作者线程,把完成端口传进去线程里
3、创建侦听SOCKET,把SOCKET和完成端口关联起来
4、创建PerIOData,向连接进来的SOCKET投递WSARecv操作
5、线程里所做的事情:
a、GetQueuedCompletionStatus,在退出的时候就可以使用PostQueudCompletionStatus使线程退出
b、取得数据并处理
4、例程
下面是服务端的例程,可以使用《WinSocket模型的探讨——Overlapped模型(一)》中的客户端程序来测试次服务端。稍微研究一下,也就会对完成端口模型有个大概的了解了。
/*
完成端口服务器
接收到客户端的信息,直接显示出来
*/
#include "winerror.h"
#include "Winsock2.h"
#pragma comment(lib, "ws2_32")
#include "windows.h"
#include <iostream>
using namespace std;
/// 宏定义
#define PORT 5050
#define DATA_BUFSIZE 8192
#define OutErr(a) cout << (a) << endl /
<< "出错代码:" << WSAGetLastError() << endl /
<< "出错文件:" << __FILE__ << endl /
<< "出错行数:" << __LINE__ << endl /
#define OutMsg(a) cout << (a) << endl;
/// 全局函数定义
///
//
// 函数名 : InitWinsock
// 功能描述 : 初始化WINSOCK
// 返回值 : void
//
///
void InitWinsock()
{
// 初始化WINSOCK
WSADATA wsd;
if( WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
{
OutErr("WSAStartup()");
}
}
///
//
// 函数名 : BindServerOverlapped
// 功能描述 : 绑定端口,并返回一个 Overlapped 的Listen Socket
// 参数 : int nPort
// 返回值 : SOCKET
//
///
SOCKET BindServerOverlapped(int nPort)
{
// 创建socket
SOCKET sServer = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
// 绑定端口
struct sockaddr_in servAddr;
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(nPort);
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sServer, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0)
{
OutErr("bind Failed!");
return NULL;
}
// 设置监听队列为200
if(listen(sServer, 200) != 0)
{
OutErr("listen Failed!");
return NULL;
}
return sServer;
}
/// 结构体定义
typedef struct
{
OVERLAPPED Overlapped;
WSABUF DataBuf;
CHAR Buffer[DATA_BUFSIZE];
} PER_IO_OPERATION_DATA, * LPPER_IO_OPERATION_DATA;
typedef struct
{
SOCKET Socket;
} PER_HANDLE_DATA, * LPPER_HANDLE_DATA;
DWORD WINAPI ProcessIO(LPVOID lpParam)
{
HANDLE CompletionPort = (HANDLE)lpParam;
DWORD BytesTransferred;
LPPER_HANDLE_DATA PerHandleData;
LPPER_IO_OPERATION_DATA PerIoData;
while(true)
{
if(0 == GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (LPDWORD)&PerHandleData, (LPOVERLAPPED*)&PerIoData, INFINITE))
{
if( (GetLastError() == WAIT_TIMEOUT) || (GetLastError() == ERROR_NETNAME_DELETED) )
{
cout << "closing socket" << PerHandleData->Socket << endl;
closesocket(PerHandleData->Socket);
delete PerIoData;
delete PerHandleData;
continue;
}
else
{
OutErr("GetQueuedCompletionStatus failed!");
}
return 0;
}
// 说明客户端已经退出
if(BytesTransferred == 0)
{
cout << "closing socket" << PerHandleData->Socket << endl;
closesocket(PerHandleData->Socket);
delete PerIoData;
delete PerHandleData;
continue;
}
// 取得数据并处理
cout << PerHandleData->Socket << "发送过来的消息:" << PerIoData->Buffer << endl;
// 继续向 socket 投递WSARecv操作
DWORD Flags = 0;
DWORD dwRecv = 0;
ZeroMemory(PerIoData, sizeof(PER_IO_OPERATION_DATA));
PerIoData->DataBuf.buf = PerIoData->Buffer;
PerIoData->DataBuf.len = DATA_BUFSIZE;
WSARecv(PerHandleData->Socket, &PerIoData->DataBuf, 1, &dwRecv, &Flags, &PerIoData->Overlapped, NULL);
}
return 0;
}
void main()
{
InitWinsock();
HANDLE CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
// 根据系统的CPU来创建工作者线程
SYSTEM_INFO SystemInfo;
GetSystemInfo(&SystemInfo);
for(int i = 0; i < SystemInfo.dwNumberOfProcessors * 2; i++)
{
HANDLE hProcessIO = CreateThread(NULL, 0, ProcessIO, CompletionPort, 0, NULL);
if(hProcessIO)
{
CloseHandle(hProcessIO);
}
}
// 创建侦听SOCKET
SOCKET sListen = BindServerOverlapped(PORT);
SOCKET sClient;
LPPER_HANDLE_DATA PerHandleData;
LPPER_IO_OPERATION_DATA PerIoData;
while(true)
{
// 等待客户端接入
//sClient = WSAAccept(sListen, NULL, NULL, NULL, 0);
sClient = accept(sListen, 0, 0);
cout << "Socket " << sClient << "连接进来" << endl;
PerHandleData = new PER_HANDLE_DATA();
PerHandleData->Socket = sClient;
// 将接入的客户端和完成端口联系起来
CreateIoCompletionPort((HANDLE)sClient, CompletionPort, (DWORD)PerHandleData, 0);
// 建立一个Overlapped,并使用这个Overlapped结构对socket投递操作
PerIoData = new PER_IO_OPERATION_DATA();
ZeroMemory(PerIoData, sizeof(PER_IO_OPERATION_DATA));
PerIoData->DataBuf.buf = PerIoData->Buffer;
PerIoData->DataBuf.len = DATA_BUFSIZE;
// 投递一个WSARecv操作
DWORD Flags = 0;
DWORD dwRecv = 0;
WSARecv(sClient, &PerIoData->DataBuf, 1, &dwRecv, &Flags, &PerIoData->Overlapped, NULL);
}
DWORD dwByteTrans;
PostQueuedCompletionStatus(CompletionPort, dwByteTrans, 0, 0);
closesocket(sListen);
}
WinSocket模型的探讨——完成端口模型相关推荐
- Windows Sockets网络编程(5)完成端口模型(IOCP)
摘要:上篇文章<Windows Sockets网络编程(4)套接字重叠IO模型>中,讲到了重叠IO的模型,同时也提到了APC函数,重叠IO是通过注册APC函数让线程调用来实现的,细心的你会 ...
- Winsock 完成端口模型简介
摘自<Networking Programming for Microsoft Windows>第八章 "完成端口"模型是迄今为止最为复杂的一种I/O模型.然而,假若一 ...
- linux socket完成端口,“完成端口”模型(IOCP)
// /// "完成端口"模型(IOCP)(基于重叠端口(异步)的LF线程池框架) /// I/O完成端口是应用程序使用线程池来进行处理异步的I/O请求的一种机制. /// 使用I ...
- 劳动力工资调整模型的探讨——数学建模
劳动力工资调整模型的探讨--数学建模 近年来,"用工荒"在全国范围蔓延,似乎成为一个风潮.虽然劳动力价格一路飙升,一 方面工厂招工难.留人难.另一方面每年有大量的新生就业人员找不到 ...
- 高性能的 socket 通讯服务器(完成端口模型--IOCP)
2019独角兽企业重金招聘Python工程师标准>>> 很多人费尽心思,都没有找到一个完美的 I/O CP 例程,甚至跟人于误解,先将本人编写的例程公布出来,希望对那些苦苦寻觅的人带 ...
- 一个对Winsock完成端口模型封装的类
转载请按如下方式显示标明原创作者及出处,以示尊重!! 原创作者:elssann 联系方式:PPP elssann@hotmail.com 在Windows下进行网络服务端程序开发,毫无疑问,Winso ...
- 关于深度学习人工智能模型的探讨(八)(1)
第八章 深度学习模型 8.1 深度学习AI 2012年6月,<纽约时报>披露了Google X实验室的"谷歌大脑"项目,研究人员随机提取了1000万个静态图像,将其输入 ...
- C++小项目(聊天室)——select模型+mysql+花生壳端口映射打造一个可以用外网连接的小qq
成品展示: B站视频链接 这个小软件是我初学网络编程写的小玩具,记录一下,等学完完成端口模型再利用完成端口写别的好玩的软件,看的课程是这个老师,真的强烈推荐,课程28块钱,老师讲的巨棒,很细,很适合新 ...
- 保存模型后无法训练_模型构建到部署实践
导读 在工业界一般会采用了tensorflow-serving进行模型的部署,而在模型构建时会因人而异会使用不同的深度学习框架,这就需要在使用指定深度学习框架训练出模型后,统一将模型转为pb格式,便于 ...
最新文章
- 台湾ML笔记--1.2 formalize the learning probelm
- java程序一写文件就崩溃_为什么直接修改java的.class文件会导致程序崩溃
- 11. 旋转数组的最小数字
- HPUX11.31环境下,更换HBA卡后的配置操作(HP-UX)
- 性能监控工具——Cacti安装文档
- 清理神器CleanMyMac 6.18“粽”级优惠
- CSS控制文本超出指定宽度显示省略号和文本不换行
- Tableau Desktop 2020 Mac支持M1芯片big sur 解决M1芯片安装Tableau闪退问题教程Tableau Public
- 解决 Win 10 输入法(仅桌面)的问题
- 求职数据分析师岗位,简历应该如何写?|工科生三个月成功转行数据分析心得浅谈
- html自动排版 vs,Visual Stidio Code (vscode)自定义HTML页面自动生成格式
- AWS CloudFront / 亚马逊CDN使用教程
- Flutter 清除应用缓存
- 微信浏览器打开ios App Store 并且可以打开或下载pp
- 配置Ubuntu以及ssh
- Excel与PowerBI 之PowerQuery 编辑界面异同-PowerQuery 系列文章之三
- 怎么用clear case?
- 下列哪项不是python中对文件的读取操作-Python—文件读写操作
- Redis的配置及与memcached区别
- 初级软件测试面试前需要做什么准备?面试题可以去哪里找?
热门文章
- java i线程安全吗_Java中 i++ 是线程安全的么?为什么?
- 二、ElasticSearch内置分词器
- 产品经理需要掌握的9种共性推荐策略
- 云游戏关键技术研究报告(2020年)
- 互联网日报 | 美团门票单日入园人次破500万;蔚来用户累计换电百万次;2020诺贝尔生理学或医学奖揭晓...
- 需求分析——识别系统需求中的三类事件
- 小白上手Mysql数据库指南~~
- Microsoft 推出在AzureApp Service上支持Windows容器的公开预览版
- php性能优化 --- laravel 性能优化
- laravel 错误与日志