您的位置:首页 > 其它

我的IOCP学习笔记

2014-05-31 19:35 13 查看
    简单介绍CodingNote/IOCP目录下两个工程EasyIocp和IocpDemo。由于这两个工程使用的线程池都是NT6线程池,所以要和XP说拜拜咯。
    

    编写IOCP逻辑确实挺麻烦的。麻烦之一是IOCP本身异步的机制、麻烦之二是IOCP相关的API的使用,麻烦之三就是用这些API编写收发数据逻辑。因此我的学习方式是一步一个脚印,一步一个笔记。
    
     其实我在12年的11月份曾改编过一个IOCP示例,为那个示例添加了WSASend接口。但是我不喜欢那个示例,因为那个示例中PER_SOCKET_CONTEXT和PER_IO_CONEXT很不好让人理解。去年9月份我转行做游戏服务器开发了,在14年三月份时也就想自己实现一个小的逻辑服务器用来编写并测试一些游戏服务器中相关的逻辑,于是我从新捡起了IOCP。

    我觉得刚接触IOCP时可以先试着了解一下QueueUserAPC这个函数,它可以向线程(假设此线程是ThreadA)插入用户APC函数。ThreadA线程处于警报状态时便会执行插入的APC函数。用QueueUserAP写一个简单小例子可以感受一下Windows异步调用。下面是简单说明一下,ThA和ThB是两个不同的线程。

    ThA --(apcTest)--> ThB --(SleepEx)--> apcTest

                                                    v-------------
                                                    >--> SleepEx返回WAIT_IO_COMPLETION--> ThB继续执行

        首先,ThB调用SleepEx函数并且设置bAlertable参数为TRUE,这样一直等待。
        然后,线程ThA调用QueueUserAPC插入apcTest函数到ThB。
        这样,ThB便会执行apcTest函数,执行完后从SleepEx函数返回(返回值为WAIT_IO_COMPLETION)继续往下执行。

    感受了下Windows异步过程调用,下面就是了解IOCP相关的API的使用。关于IOCP本身我把IocpDemo就作为了我的学习笔记,所以从代码中学习吧。然后比较懒就不详细说明其中的细节咯。在这一步我写了IocpDemo。这个工程有如下子工程:
    1)fileIocp展示IOCP写文件例子,之后再同步的读取刚写入的文件。
    2)acceptEx和acceptExClient展示了AcceptExGetAcceptExSockaddrs使用。
    3)transport和transportClient展示了WSASendWSARecvaccept使用(注:MSDN文档有所调用socket函数创建的套接字默认就会有重叠属性WSA_FLAG_OVERLAPPED,因此accpet返回的套接字也一样)。

    另外学习IOCP前可以先阅读MSDN关于CreateIoCompletionPort、GetQueuedCompletionStatus和PostQueuedCompletionStatus说明。这三个API构成了使用IOCP基础。
    调用CreateIoCompletionPort创建完成端口,当完成端口中有数据包到来时调用GetQueuedCompletionStatus来获取完成的数据包。当想关闭完成端口时可以调用PostQueuedCompletionStatus发送特殊的数据,检测到特殊的数据到达时则退出线程,不再调用GetQueuedCompletionStatus。在某种意义上可以把GetQueuedCompletionStatus想象成是GetMessage,可以把PostQueuedCompletionStatus想象成是PostMessage。
    GetQueuedCompletionStatus把网络数据与线程关联,GetMessage把窗口消息与线程关联。当有网络数据/窗口消息到达时,相应的便会返回,让程序处理到来的数据/消息。

    了解了IOCP的相关的API,于是就正式的编写IOCP - EasyIocp。EasyIocp通信模型是"4字节报头+数据"。4字节报头header表示数据body的字节数

    EasyIocp分为如下几个小模块:

     1)客户端管理:类EasyClients。
     2)CompletionKey管理:基类CompletionKey,两个派生类分别是CompListenKey和CompTransmitKey。
     3)错误处理:类EasyIocpError。
     4)数据收发:在EasyIocp中处理。

     5)线程池:使用的NT6线程池,在EasyIocp中处理。
 
    调用CreateIoCompletionPort绑定完成端口与套接字时,参数CompletionKey是与套接字关联的。
    编程时EasyIocp把套接字分为两种监听套接字,与数据收发套接字。监听套接字对应的CompletionKey则是CompListenKey,而数据收发套接字对应的则CompTransmitKey。

    大致就这么多咯,剩下的在代码中找细节吧。看代码着带着问题:套接字的初始化,建立连接,接收数据,发送数据,数据是否接收完毕,数据是否发送完毕,如何安全的关闭。由于我只是简单的测试下,代码中肯定有Bug,童鞋们靠你们了。

    EasyIocp还有很多不完善的地方,同学们可以自己完善奥。

1)接收数据时,没有设置接收超时时间。
2)采用报头,向绑定的端口发送数据时前4字节任意设置,EasyIocp仅仅是简单的处理,投递Recv时接收数据的字节数不超过缓冲区的大小。
3)EasyIocp没有导出更便捷的接口让业务层使用,我仅仅是用PushToDebug作为测试。

    最后,由于自己封装接口还需要更深的功底,所以我下面计划使用第三方库来实现数据收发。

 History
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息