时间过了真快,一下子到了2014年5月1号了,国家法定节假日,我相信肯定有很多同仁和我一样,在这举国同庆的日子里,还在.....
今天看了下IO完成端口的相关内容,现在趁自己脑子还是比较清爽的时候,把自己的一点收获稍微记录下,方便自己在以后用的时候可以查看,如果自己的读后感能够帮助到正在看的你,那也是我的荣幸,现在总觉得有这么个感觉,有的时候单纯的看理论方面的东西比较抽象,如果在看些理论后,能够再看时间代码例子,那个叫豁然开朗。
1. 在知道了完成端口的一些基本概念后,首先需要掌握的是完成端口的基本框架,下图即为单独启了个线程用来accept外接socket的系统框图。
2. 由于上述的单独启了线程用来接收外来的socket,当外来并发的接入请求很少时,效率问题并没有体现出来,当并发请求很多时,那问题就随之而来了,下图就是采用acceptEx来代替accept的解决方案
3. 书上介绍第二种方式比第一种方式的好处在于以下三点, 首先socket创建,在我们看来只是利用一句话就能创建出来,但是系统做了很多事情,尤其并发创建的时候,工作量更是惊人,第二种方式是在用户接入前就把socket创建好了,等待用户进来,这就比第一种就好多了,其次就是第二种在accept的同时,能够顺便接收用户发送过来的第一条消息,第三,第二种方法能够同时抛出多个accept,这样速度上面又能够提高不少。
4. 在完成端口的整个框架过程当中所需要使用到的几个关键地方,1)服务器先开始肯定要创建用于接收用户的申请的socket, 在这里要创建一个PER_SOCKET_CONTEX与之对应,因为在IO绑定的过程当中需要将这个PER_SOCKET_CONTEX作为其中的一个参数,怎样把socket和这个PER_SOCKET_CONTEX联系起来呢,就是将socket赋给PER_SOCKET_CONTEX变量的socket成员,底下的工作就是将这个PER_SOCKET_CONTEX绑定起来了, 但是这个socket是用来监听的,这时就需要创建一个sockaddr_in变量,并且将这个sockaddr_in变量和socket绑定起来,然后在这个socket进行监听。
5. 上述的任务只是完成了监听工作的一半,因为我们还没有投递accept请求,投递请求前,我们需要通过调用PER_SOCKET_CONTEX中的GetNewIoContext函数创建一个PER_IO_CONTEX实例,剩下的工作就是对PER_IO_CONTEX中的成员变量进行相关赋值了,首先上述说过acceptEx这种方式是先创建socket,这个过程就是在这个地方,我们在这个地方就创建一个socket,并且把这里创建的socket赋值给PER_IO_CONTEX中的acceptSocket实例,还要把我们投递的消息类型赋值给PER_IO_CONTEX实例中的msgType,剩下的就可以通过acceptEx把上面组装好的PER_IO_CONTEX投递出去了。
6. 从上面的图中我们可以看出到目前为止,主线程已经没有什么工作了,下面的工作就是到工作者线程中,在线程函数中我们主要用到函数就是GetQueuedCompletementStatues函数得到里面的两个重要成员, 一个是PER_SOCKET_CONTEX实例,还有一个就是overlapped实例,除了上面两个有用信息外,还可以通过一些变量状态判断不同状态,如果上面函数中得到的PER_SOCKET_CONTEX的变量值EXIT_CODE,那就说明线程可以退出了,如果在函数返回值为false,那么就说明程序在执行的过程当中出了错误,如果函数中输出参数中表示接受数据大小的值为0,并且消息类型为send或者recv的话,那就说明当前的用户已经离线了,可以将其PER_SOCKET_CONTEX实例对象删除了,除了上面的情况,剩下的就是正常的情况了,首先通过函数中的overlapped实例获取PER_IO_CONTEX实例(具体是通过什么方式来获得的,我想是通过结构体的首地址方式),通过里面的msgType来判断当前投递是recv,send,accept中的哪一种。
7. 如果是accept投递,我们需要重新创建一个PER_SOCKET_CONTEX实例,首先需要将刚才PER_IO_CONTEX中获得的acceptSocket实例赋值给PER_SOCKET_CONTEX中的localSocket实例,然后将此PER_SOCKET_CONTEX与完成端口绑定起来,之后还要创建属于此PER_SOCKET_CONTEX实例的PER_IO_CONTEX实例,并且还需要将该实例中的msgType值赋以想投递的消息种类,并且还需要将overlapper中的acceptSocket实例赋值给PER_SOCKET_CONTEX实例和PER_IO_CONTEX实例中的socket成员,并且把这个IO_CONTEXT以recv的形式投递出去。
8. 在判断的过程中如果是recv投递的,那么需要把里面的数据读取出来,然后把里面的消息缓存部分清空,再次投放。
大家在看这部分的时候,先熟悉框架,然后再看里面的具体实现吧,里面有很多细节方面需要注意,如果给大家造成困扰,请见谅