概述
IOCP 完成端口最简说明
IOCP越说越复杂,我想尝试简明的从初学者的概念来说明,把最迷惑的部分简要说明:
关键点一:完成IO端口返回的消息,系统会自动保存在队列里!不是触发或者立刻返回!因为服务器需要快,所以完成端口自己先把处理好的消息全部保存到队列里!它不管你现在干嘛,只要合法的它都帮你存好!用不用你取出来再判断!所以我们关键的动作是从完成端口的队列去把消息取出来并处理!
关键点二:它的关键函数:到队列去取数据的函数,它是阻塞式的。你执行了这个函数,如果队列里没有消息,它会停住等到有消息之后才往下走(当然也可以设置马上往下走,但实际我们都会让它等,这是它效率高的关键)!所以大部分情况下,我们需要建立线程去执行从队列里取数据的动作!建立一个或者多个线程等待直到队列里有新消息并每次取出一条进行处理!多线程取的好处是取的更快(队列消息积压是糟糕的)
对,这是关键点!
简单来说只要3步
1)Var WSData: TWSAData;
WSAStartup($0202, WSData); 初始化库,后面才能调用相关完成端口函数,WSData用于判断是否初始化成功
2)var FIocpHandle: THandle;
FIocpHandle:=CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0); 这里创建了一个完成IO端口并返回其句柄
此时我们的完成IO端口建好了!只是它暂时还没有输入源(也就是没绑定文件句柄)。
系统已经开始为我们准备好队列了,只是没有绑定输入源所以是没有消息进来的。
你已经可以去尝试队列取消息了,微软为我们取消息准备好了指定的函数,不过这个函数比较特别,需要特别注意,看下一步。
3)GetQueuedCompletionStatus(CompletionPort, BytesTransferred,
ULONG_PTR(cSocket), POverlapped(PerIoData), INFINITE)
从消息队列取一个消息!第一个参数就是我们建立的完全IO端口,后面2个参数是返回消息的套接字信息,第三个是返回的数据,第四个参数是等待消息的时间,设置为INFINITE会无限时间等下去!
如前所说,这个函数是阻塞式的!当队列是空的,它就会停住等待!这个好处是节省CPU时间,坏处是我们要建立线程来执行它以免主线程阻塞。但是只要有新消息到,这个函数是马上完成并返回套接字信息和数据信息!所以可以看到,为什么每个解释完成端口的文章,都要说到线程!因为必须要建立线程来取完成端口的队列取处消息!建立的线程简单来说就是不断的执行这个函数!不用考虑任何东西,你就反复的执行这个命令来取消息来处理(根据你的业务需求处理收到的数据),没消息的话GetQueuedCompletionStatus会停住不消耗CPU时间。当然你对消息的处理越快你的服务器性能表现的越好,不过基本不用担心数据丢失!这个队列是很强大的。
完成端口建立好,消息队列也准备好了
下面需要为完成端口绑定输入的数据源(文件句柄),否则是没有消息进来的,消息队列会一直为空(这应该很好理解)。
对于TCP而言,需要跟客户端套接字完成握手后,才能收到消息。
建立服务器跟客户端的连接有如下三步:
1) WSASocket(PF_INET, SOCK_STREAM, 0, nil, 0, WSA_FLAG_OVERLAPPED); 建立一个套接字
2)bind(FSocket, @Addr, SizeOf(Addr)) 把这个套接字跟端口(PORT)绑定,因为服务器基本都是固定一个端口的
3) listen(FSocket, MaxInt) 开始监听这个套接字看有没有客户端请求连接
这三步后,服务器会开始处理所有的连接,我们不用管,系统处理好的连接也会放到队列里面!!!
又是队列!!跟上面一样,我们需要建立线程来查看连接队列是不是有新的连接请求进来!
微软为我们提供查询连接队列的函数是 : WSAAccept , 这个查看连接队列的函数跟GetQueuedCompletionStatus一样,当队列为空它会等下去!所以我们又要用线程来执行它,等到连接队列有新建立好的连接后,它才给我们返回跟客户端已经连接好的套接字。注意,是连接好的套接字,所以具体是怎么连接我们是不用处理的。
现在看来,整个完成端口,我们要做的,就是高效的的查收两个队列的信息并处理他们: 消息队列 和 连接队列。微软的设计是要求我们用线程去访问这两个队列的!所以对于完成端口模型的TCP,我们是需要设计两个线程来分别不断的访问这两个队列的。不过不用担心,如果消息队列为空,线程等待所消耗的CPU时间可以忽略不计!这就是完成端口的高效原因之一。
总结来说
WSAAccept 等待新的连接请求进队列,只要队列不为空马上取出一个处理
GetQueuedCompletionStatus 等待新的消息进队列,只要队列不为空马上取出一个处理
至于微软怎么做到这个等待不耗时我们基本不用管了。
WSAAccept查询连接队列,当队列有正确的连接请求(TCP在没有建立连接前是不接收任何其他信息的,所以我说这里是监听连接队列),WSAAccept会返回一个建立好的跟客户端对应的套接字,把这个套接字跟完成端口绑定,这样来自客户端的消息就会正确进入我们的完成端口队列了。这个绑定仍然是用前面创建完成端口的函数CreateIoCompletionPort,这次需要指定要绑定的完成端口和刚返回的客户端对应套接字。可以参考CreateIoCompletionPort函数的参数说明。
一个完成端口可以跟多个客户端套接字绑定(微软设计的客户端套接字也是可以复用的,尽量减少开套接字的开销)
整个接收完成了!!!
发送跟其他的模式没有特别的不同,这里不详细说。
根据我初学者的误区,我一直在找哪里触发连接、接收、断开的消息!不是的,真实的情况是我们需要建立线程来查询连接队列和接收消息队列!对,就是我们要处理的重点是怎么快速的从这两个队列里把消息取出来。并且需要再次强调,微软提供的从这两个队列取数据的函数,它在队列为空的时候会停住等待!所以实际应用中,建立线程来执行这两个函数是必须的。
好了,一个初学者理解到这,很多地方没有用微软给的专业名词只是为了更好的理解。
有错误理解的请告知我让我也改正。
根据这个理解写了基本没有多余的源码:一个完全端口对象,一个连接队列线程,一个信息队列线程,有些错误但可以运行了。需要的可告知。
最后
以上就是淡然机器猫为你收集整理的IOCP 完成端口的最简说明 - 附DELPHI源码的全部内容,希望文章能够帮你解决IOCP 完成端口的最简说明 - 附DELPHI源码所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复