这里描述一种后台网络服务器模型,其中涉及到侦听线程,缓冲线程,业务处理线程。侦听线程循环等待客户端的连接,一旦有客户端连接上来,就将客户端socket加入到socket队列中,缓冲线程负责循环等待客户端socket 的读信息。缓冲线程中的客户端socket就是侦听线程提供的。缓冲线程可以有多个。侦听线程和缓冲线程共同维护socket队列。
这里解释一下为什么要开多个缓冲线程。因为我们采用了select 这种 I/O 复用模型,select 在管理大量 socket的时候,在Windows 上,虽然可以通过重新定义 FD_SETSIZE 来使其能够管理大量socket,但是效率相对较低,而在linux上面,select 有FD_SETSIZE
的限制,数量多了还不行。所以这里开启多个缓冲线程,每个缓冲线程里面调用select 函数,这样,管理的 socket 数量能够增加几倍,达到 n * FD_SETSIZE。在 socket 的维护中,缓冲线程之间不需要同步,因为他们各自维护了一个 socket队列,缓冲线程只和侦听线程需要同步维护socket。
前面提到缓冲线程负责循环等待客户端socket 的读信息,当客户端socket 有读信息的时候,缓冲线程会为该客户端寻找空闲的 业务处理线程。这里采用线程池的方式来管理业务线程。如果缓冲线程能够找到空闲的业务线程,则把 当前socket 交给该空闲的业务线程,如果没找到,则创建 业务处理线程池。所以,缓冲线程 和 业务处理线程需要同步业务处理线程的工作状态,是空闲,还是正在忙于处理客户端socket 。
下面给出这种网络服务器模型的时序图:
如上图,ListenThread负责侦听客户端的连接,当有客户端连接上来时,接受socket,并且发送消息3:add socketInfo,将socketinfo增加到 m_sockCacheList[nIndex]中。nIndex表示缓冲线程的线程索引,取值可以为[1…n]。ListenThread用socket总数对缓冲线程总数取模来决定当前socket加入到哪个缓冲线程中。
缓冲线程对自己m_sockCacheList[nIndex]中的socket循环检查是否有数据可读。若有数据可读,则为客户端寻找空闲的TradeThread(或者创建TradeThread,发送消息5:Create TradeThread),接着发送消息6:Delete socketInfo,将socketInfo从自己内部的m_sockCacheList[nIndex]移除。
TradeThread,侦听客户端的请求操作,并且根据业务规则,做相应处理。当发现客户端在规定的时间内无反应时,发送消息:5:3,Delete socketInfo,将scoketInfo删除。