网络服务器开发框架spserver源码分析 (二)

     连接到服务器是进行网络通信的第一步,同时对于网络服务器来说面临的第一个问题便是如何处理大量的client接入,本文主要分析spserver中是如何处理client接入,session管理和利用超时机制避免“空连接”的问题的。

 

     上一篇中提到了SP_IcopServer负责提供服务器的用户接口,其中runforever会调用start方法,服务器的初始化和主循环都是在这个函数中完成的。由于spserver使用IOCP作为底层实现,在连接处理这里也不例外。start首先初始化一个socket用于接受client接入,并将socket与一个IOCP(其实系统所有的IO都经过同一个IOCP)绑定。绑定完成后,发起一个线程(acceptthread),专门处理client的接入。

   

    acceptthread 首先利用WasSocket新建用户连接socket,随后调用异步函数AcceptEx;同时需要注意的是,所有与连接相关的信息都存入了一个类型为SP_IocpAcceptArg_t的变量acceptArg中,AcceptEx成功完成后,会等待在acceptArg的连接事务上,这样起了类似于同步调用的效果。

 

    由于监听accept的socket与IOCP绑定,start函数主循环便会调用GetQueuedCompletionStatus来等待异步IO事件,如果此时有client接入,通过GetQueuedCompletionStatus的completion key来判断:用于监听accept的socket上用IO事件,随后调用回调函数OnAccept()。

 

   spserver为每个接入的连接都建立session来进行管理,同时为了提高性能,作者提供了session manager来管理session并为每个session都分配了一个ID,这个ID可以看作是一个key。利用这个key,可以使得session manager快速找到制定的session。(这个ID会作为针对该session操作IOCP的completion key,这样可以快速得知某个IO事件属于哪个socket)。分派完ID,利用GetAcceptExSockaddrs来获取client socket的地址信息并存在session中。我们在上篇中提到了SP_Handler 和 SP_IOChannel来提供灵活性, 这里这两者的信息也会记录在session之中,可以想象当以后针对这个session进行IO操作时,真正完成IO操作的一定是SP_IOChannel中的receive和transmit。同时在连接建立,断开,服务等时,用可以利用SP_Handler来“插入”上层模块的逻辑,这种灵活的设计也保证了spserver的易用性。

 

    在session记录完信息后,就会调用CreateIoCompletionPort来将client socket与IOCP绑定,之后就会调用SP_Handler内的start函数。这里作者将这个函数的调用包装成任务,利用线程池来运行一个简单任务来完成上层应用的逻辑。

 

    如何用户连接后一直不进行通信,这样的连接白白消耗服务器资源,所以必须关闭;或者在通信一段后,client端出现问题,连接超时,那么该如何甄别这些无效连接呢?spserver是这样解决这个问题的:每当发起一个IO操作,就把这个IO操作记录在一个SP_IocpEvent_t的类型中,这个类型会记录当前的IO操作的超时的绝对时间(IO发起时间+超时忍耐时间),然后利用小顶堆来管理所有的IOevent事件。在服务器的主循环内GetQueuedCompletionStatus的超时时间,就设置为当前顶堆event的超时时间与当前时间的差,这样只要判断GetQueuedCompletionStatus是不是超时退出,如果是,那么顶堆的这个event一定就超时了,这样就可以对这个IO操作通知失败进而关闭连接。

 

   下一篇将主要分析接收数据,发送数据的实现。

 

 (未完待续。。。)

 

   

    

你可能感兴趣的:(网络服务器开发框架spserver源码分析 (二))