易语言教程_易语言源码_易语言视频教程_易语言论坛

 找回密码
 点击注册

Vip新手入门区
新手学习指南  学员作品展示 Vip课程总纲  Vip绝密课程系列

Vip相关下载区
Vip模块下载   Vip模块绑定   Vip模块例子 魔鬼插件下载  魔鬼插件例子  教程工具下载

Vip论坛服务区
教程问题提问区   模块问题提问区 技术交流区   魔鬼插件建议   忘记密码找回

VIP会员办理QQ: 8643245   
【请先加好友,然后到好友列表双击联系客服,办理VIP会员。】
【基础篇】易语言辅助入门基础教程
VIP模块办理QQ: 7189694 办理正版魔鬼作坊VIP模块 【基础篇】OD与CE入门基础教程
办理【终身VIP会员】“秒杀价” 仅需 RMB278.00元… 【基础篇】零基础绝密汇编语言入门课程 (共26课已完成)…
办理VIP详情…猛击这里查看详情 【基础篇】VIP辅助入门基础教程-新手必学 已发布10课 ……
VIP教程免费试看章节…猛击下载 【第1款】制作“辅助挂”教程目录查看(共107+16_x64下更新课已完成)…
亲爱的VIP学员,请到此写下你学习的感受与发布作品截图… 【第2款】制作“任务挂”教程目录查看(共77+1_x64下更新课已完成)…
卍解吧!不用bp send类封包断点找CALL的各种通杀思路 【第3款】驱动过保护技术课程(共38课已完成)…
【绝密教程】VIP绝密教程系列---注意:随时会更新! 【第4款】VIP邪恶二叉树辅助课程 (共31+17_x64下更新课已完成)…
【精品第13款】3D射击游戏与页游透视 智辅课程 已完成17课… 【第5款】零基础易语言按键辅助教程 (30课已完成)…
【精品第14款】变态功能辅助是如何炼成的 已完成36课… 【第6款】从零开始学习封包辅助技术教程(20课已完成) …
【精品第15款】DNF商业变态辅助的修炼之路 已完成27课… 【第7款】大杀特杀分析来源与CALL吸血鬼课程 (56课已完成)
【精品第16款】中控台多线程多开自动化商业辅助课程 已完成66课… 【第8款】完全零基础网页辅助课程(40课已完成)
【全新精品第17款】检测原理与过游戏内存检测技术课程 已发布9课… 【第9款】自动登录与操控LUA技术课程 (共46+8_x64下更新课已完成)…
【全新精品第18款】手游全自动化任务脚本辅助课程 已发布25课…… 【第10款】网页辅助封包脱机进阶课程 已完成30课…
【全新精品第19款】D3D方框骨骼透视与自瞄辅助课程进阶篇 已发布34课…… 【第11款】VC++ Lua脚本辅助课程 已完成112课…
【全新精品第20款】 X64模拟器吃鸡游戏方框透视自瞄辅助课程 发布中... 【第12款】网游脱机封包智辅课程 已完成35课…
查看: 1460|回复: 0

IOCP异步TCP服务器模型讲解

[复制链接]

20

主题

4

回帖

32

积分

编程入门

Rank: 1

魔鬼币
574
发表于 2016-1-12 19:03:11 | 显示全部楼层 |阅读模式
注意:阅读本文章需要有一定的winsock开发基础。

例程源码在最下面在众多的服务器模型中IOCP异步模型无疑是最高效的服务器模型,是其他模型无法比拟的,一些高效稳定的服务器基本都是用IOCP模型的 例如 Apache。

什么是IOCP?IOCP我们可以将它看成一个队列  I/O是一个硬件设备详情介绍请看百度百科。

我们先用普通同步模型的服务器结构做对比

同步模型流程:

1.WSAStartup() 加载winsock服务
2.Socket()/WSASocket() 创建一个套接字
3.Listen() 设置监听模式
4.建立一个线程进行无限循环的Accept()
5.Accept得到连接后建立一个线程与客户通讯

这样的流程很明显并不高效,因为每个客户都要创建一个线程与之通讯,当接受到大量的客户进入的时候就挂了。

那么IOCP模型是怎样的呢? 。

IOCP模型:

1.WSAStartup() 加载winsock服务
2.Socket()/WSASocket() 创建一个套接字
3.CreateIoCompletionPort() 创建一个完成端口
4.Listen() 设置监听模式
5.CreateIoCompletionPort() 将sock与IOCP绑定
6.建立Worker线程(具体这个线程中是干什么的看下面)
7.建立Accept线程(为了更好的讲解暂时使用Accept,下面将介绍扩展的 AcceptEx)

这样看起来两个模型并没有什么区别,但重点在下面

同步模型的Accept线程在得到一个客户连接后将会建立与其通讯的线程;
与同步模型的Accept线程不同的是IOCP模型的在得到客户连接后将会把客户的Socket绑定到IOCP然后投递一个WSARecv操作

IOCP的重点来了 同步模型的Recv操作是同步的,需要等待接到消息或者超时后才返回,但IOCP模型的是投递一个Recv操作  注意 是投递!

那么为什么叫投递呢?  我们来对比上面说了同步模型的Recv需要等待接到消息或者超时后才返回,而IOCP模型的则是投递一个WSARecv操作,这个WSARecv操作将会由系统来帮你完成;当系统接收到消息后将会把消息放到IOCP里面,我们只需要调用 GetQueuedCompletionStatus()来获取消息。

那么我们来看看Worker线程都干了些什么?

很简单 Worker线程只不过是在不断的调用GetQueuedCompletionStatus() 获取来自IOCP的消息
然后根据消息做出不同操作。这里我们暂时不提,因为我们还不知道他是如何获取消息的。

下面讲解 WSARecv() 和 GetQueuedCompletionStatus() 以及IOCP模型最重要的重叠结构!

WSARecv()  用于接收套接口的消息
参数1:s 客户的套接字
参数2:pBuffers  一个WSABUF结构 WSABUF 有2个成员 成员1:len 表明缓冲区长度 成员2:一个指针地址 指向接收数据的缓冲区  这个参数可以是一个数组 因为他可以一次接收多个消息。或者你也可以不传入数组到那下面的参数将要设置为1
参数3:wBufferCount WSABUF数组的数量
参数4:lpFlags  和用来控制套接字的行为 通常设置为0
参数5:lpOverlapped 重叠结构指针地址 重要! 下面讲解
参数6:lpCompletionRoutine 这个我们之间设置0 不管他

GetQueuedCompletionStatus() 用于接收IOCP的消息
参数1:CompletionPort  IOCP
参数2:NumberOfBytesTransferred  整数型传址 用于接收IOCP得到的消息的长度
参数3:CompletionKey  这类似一个线程中的参数 是由 PostQueuedCompletionStatus()传递过来的
参数4:lpOverlapped   重叠结构指针地址
参数5:dwMilliseconds 超时时间 -1表示无限等待

重叠结构:

.数据类型 OVERLAPPED
    .成员 Internal, 整数型
    .成员 InternalHigh, 整数型
    .成员 offset, 整数型
    .成员 OffsetHigh, 整数型
    .成员 hEvent, 整数型

这是基本的重叠结构 这些我们都不需要管,重叠结构之所以最重要是因为我们传递很多东西都要靠它了,例如接收的数据是 WSARecv操作还是AcceptEx操作或者WSASend操作,还有接收数据的缓冲区指针地址啊之类的,很多东西我们可以靠它来传送。

首先我们看 WSARecv() 他的参数5是一个重叠结构的指针地址,而我们使用的GetQueuedCompletionStatus()参数4
就是用来接收这个指针地址的,那么我们这样想只要这个内存区没被释放,我们就可以操作他,那么我们可以在基本的重叠结构后面加上一些我们自己的数据,这样我们在使用GetQueuedCompletionStatus()得到重叠结构地址的时候那么我们的数据不都一样全都传过来了! 这是个非常好的东西。那么我们怎么用呢?

首先我们使用HeapAlloc来为程序分配一块内存,这块内存的长度我们根据要传入的数据来做决定 这里我们示例分配48字节的内存。

其结构:

OVERLAPPED 结构 20字节 这是固定的必须的 留给操作系统 我们不管
MsgType    消息类型,用于我们区分是AcceptEx投递的操作还是WSARecv等投递的操作 4字节 整数型
C_Socket   存放客户套接字 4字节 整数型
PeerAddr   远端客户的地址信息 16字节
Buf_Ptr    缓冲区的内存指针地址  4字节 整数型

好了,我们该投递WSARecv操作了。

WSARecv (C_Sock, WSABuf, 1, NumberOfBytesRecvd, Flags, Overlapped, 0)

我们这样的调用WSARecv来投递一个Recv操作,WSABUF结构中的 缓冲区指针地址成员我们可以使用HeapAlloc来分配一块内存 这里我们给他分配4096字节(即4KB)

我们再创建一个48字节的内存块 并写入 以上介绍的数据结构中的所需数据 再将这个内存块的地址放入WSARecv的参数lpOverlapped 这样我们就投递了一个Recv操作,接下来他将由系统完成。

在Worker线程中我们使用GetQueuedCompletionStatus()来获取IOCP中的消息,它得到的重叠结构的指针地址将与我们创建的一样 所以我们可以从这块内存中取出 我们事先写入的数据 我们先取出 PeerAddr 和C_Socket得到这个客户的地址信息和sock,接下来我们取出 Buf_Ptr 我们之前在这里写入了一块内存的指针地址并作为WSABUF中的成员传入了WSARecv的参数 这时 WSARecv所接收到的数据将写入这块内存。
然后我们需要再次投递一个WSARecv操作以便接收下个来自此客户的数据。

关于Worker线程的数量 一般认为最合适的是 CPU核心数×2 因为CPU只有那么些核心太多了Worker线程也没用,乘以2是因为如果某个Worker线程处在Sleep状态那么另一个便可以代替他,从而更好的利用CPU。

AcceptEx的使用:

Accept()与AcceptEx()最大的区别在于AcceptEx可以像WSARecv一样投递给IOCP 并且还能更好的利用Socket资源减少写创建Socket所浪费的时间。

AcceptEx()
参数1:sListenSocket  服务监听的套接字
参数2:sAcceptSocket  一个将用于新客户的套接字
参数3:lpOutputBuffer 一个内存缓冲区 这个内存缓冲区将用于接收客户的地址信息 和接收客户发来的第一条消息 (如果指定了的话) 这个参数必须指定 否则将返回错误
参数4:dwReceiveDataLength  指定欲接收来自新客户的第一条消息的缓冲区长度 若此值是零 则windows不会等待新客户的第一条消息 而是立即建立连接
参数5:dwLocalAddressLength  为本地地址信息保留的字节数。此值必须比所用传输协议的最大地址大小长16个字节。
参数6:dwRemoteAddressLength为远程地址的信息保留的字节数。此值必须比所用传输协议的最大地址大小长16个字节。 该值不能为0。
参数7:dwBytesReceived 此参数只在同步模式下起作用,所以我们直接给个0
参数8:lpOverlapped    重叠结构指针地址

使用了AcceptEx的话,我们就不必再去创建Accept的线程而是直接投递一个Accept操作。当有客户连接的时候Windows会将消息写入IOCP。我们便可以从调用AcceptEx时指定的缓冲区使用GetAcceptExSockaddrs()取出客户的地址信息。在从重叠结构冲取出我们所需的信息即可。

当Worker接收到AcceptEx消息时,我们需要再次使用同样的方法投递一个Accept操作。
那么为什么说AcceptEx()能更好的利用Socket资源呢?
注意看AcceptEx的第二的参数 与Accept不同的是 Accept接受到连接系统会自动的为客户创建一个Socket 而AcceptEx则是由自己传入一个已创建的Socket 那么我们就可以使用Socket池技术 以减少Socket创建所浪费的时间
可惜的是本文章中的例程并未用到此技术。

PostQueuedCompletionStatus()的使用

PostQueuedCompletionStatus()可以向IOCP发送一条消息以便Worker线程做出相应的处理,例如我们要退出程序了而Worker线程还是处在无限循环状态,我们就可以通过这个函数发送消息给Worker让他退出。

PostQueuedCompletionStatus()
参数1:CompletionPort IOCP
参数2:dwNumberOfBytesTransferred 指定发送的消息的字节数
参数3:dwCompletionKey   这类似于线程的参数,将会传递到GetQueuedCompletionStatus()中的参数CompletionKey
参数4:lpOverlapped  重叠结构指针地址

同样的是Send操作也可以投递给系统去完成 函数声明如下

WSASend()

参数

s:标识一个已连接套接口的描述字。
lpBuffers:一个指向WSABUF结构数组的指针。每个WSABUF结构包含缓冲区的指针和缓冲区的大小。
dwBufferCount:lpBuffers数组中WSABUF结构的数目。
lpNumberOfBytesSent:如果发送操作立即完成,则为一个指向所发送数据字节数的指针。
dwFlags:标志位。
lpOverlapped:重叠结构指针
lpCompletionRoutine:一个指向发送操作完成后调用的完成例程的指针。(对于非重叠套接口则忽略)。

现在我们再回观全文 你是否发现了IOCP模型与同步模型的差距? 同步模型需要为每个客户都建立一个线程,这将极大的消耗系统资源! 而IOCP模型仅仅只需要一个或几个线程就可以完美的解决!


您需要登录后才可以回帖 登录 | 点击注册

本版积分规则

魔鬼作坊|易语言教程|易语言源码|易语言论坛|易语言视频教程| 论坛导航|免责申明|手机版||网站地图
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表魔鬼作坊立场!
任何人不得以任何方式翻录、盗版或出售本站视频,一经发现我们将追究其相关责任!
我们一直在努力成为最好的编程论坛!
Copyright© 2010-2019 All Right Reserved.
快速回复 返回顶部 返回列表