网络socket服务器开发几种并发模型详解

目录

一、socket创建流程。

二、I/O多路复用

三、服务器开发常见的并发模型

1、模型一:单线程——无IO复用

1.1 模型分析

2、模型二:单线程accept + 多线程读写业务(无IO复用)

模型分析

3、模型三:单线程多路IO复用

模型分析

 4、模型四:单线程多路IO复用 + 多线程业务工作池

模型分析

优点

区别

5、模型五:单线程多路IO复用 + 多线程多路IO复用(线程池)(实际中最常用)

模型分析

优点

缺点

6、模型六:(多进程版)单线程多路IO复用 + 多进程多路IO复用(进程池)

模型分析


一、socket创建流程。

        socket 是内核向应用层提供的一套网络编程接口,用户基于 socket 接口可开发自己的网络相关应用程序。套接字(socket)是 Linux 下的一种进程间通信机制,socket IPC 通常使用客户端服务器这种模式完成通信,多个客户端可以同时连接到服 务器中,与服务器之间完成数据交互。

        socket 是应用层与 TCP/IP 协议通信的中间软件抽象层,它是一组接口。socket 其实就是一个门面模式,它把复杂的 TCP/IP 协议隐藏在 socket 接口后面,对用户来说,一组简单的 接口就是全部,让 socket 去组织数据,以符合指定的协议。所以,我们无需深入的去理解 tcp/udp 等各种复 杂的 TCP/IP 协议,socket 已经为我们封装好了,我们只需要遵循 socket 的规定去编程,写出的程序自然遵 循 tcp/udp 标准的。

编写服务器应用程序的流程如下:

        ①、调用 socket()函数打开套接字,得到套接字描述符;

        ②、调用 bind()函数将套接字与 IP 地址、端口号进行绑定;

        ③、调用 listen()函数让服务器进程进入监听状态;

        ④、调用 accept()函数获取客户端的连接请求并建立连接;

        ⑤、调用 read/recv、write/send 与客户端进行通信;

        ⑥、调用 close()关闭套接字。

服务器程序通常需要处理三类事件:I/O 事件、信号及定时事件。


二、I/O多路复用

1、阻塞式 I/O和非阻塞式 I/O

        阻塞式 I/O 的优点在于能够提升 CPU 的处理效率,当自身条件不满足时,进入阻塞状态,交出 CPU 资源,将 CPU 资源让给别人使用;而非阻塞式则是抓紧利用 CPU 资源,譬如不断地去轮训,这样就会导致 该程序占用了非常高的 CPU 使用率。(阻塞式 I/O无法实现并发读取)

        I/O 多路复用一般用于并发式的非阻塞 I/O,也就是多路非阻塞 I/O。

2、select:最大限制(select默认1024)、轮询查看、效率较低

      poll:最大限制默认与内存有关、轮询查看、效率较低

      对于 select()或 poll()函数来说,内部实现原理其实是通过轮训的方式来检查多个文件描述符是否可执 行 I/O 操作,所以,当需要检查的文件描述符数量较多时,随之也将会消耗大量的 CPU 资源来实现轮训检查操作。

       epoll:最大限制默认与内存有关,内核会主动发送信号通知。当需要检查大量文件描述符时,可以使用 epoll 解决 select()或 poll()性能低的问题。


三、服务器开发常见的并发模型

1、模型一:单线程——无IO复用

1.1 模型分析
  • ①主线程执行阻塞accept,每次客户端connect请求连接过来,主线程中的accept响应并建立连接

  • ②创建连接成功之后,得到新的套接字文件描述符cfd(用于与客户端通信),然后在主线程串行处理套接字读写,并处理业务。

  • ③在②的处理业务时,如果有新的客户端发送请求连接,会被阻塞,服务器无响应,直到当前的cfd全部业务处理完毕,重新回到accept阻塞监听状态时,才会从请求队列中选取第一个lfd进行连接。

2、模型二:单线程accept + 多线程读写业务(无IO复用)

模型分析
  • ①主线程执行accept阻塞监听,每当有客户端connect连接请求过来,主线程中的accept响应并且与客户端建立连接

  • ②创建连接成功后得到新的cfd,然后再thread_create一个新的线程用来处理客户端的读写业务,并且主线程马上回到accept阻塞监听继续等待新客户端的连接请求

  • ③这个新的线程通过套接字cfd与客户端进行通信读写

  • ④服务器在②处理业务中,如果有新客户端发送申请连接过来,主线程accept依然会响应并且简历连接,重复②过程。

3、模型三:单线程多路IO复用

模型分析
  • ①主线程main thread 创建 lfd之后,采用多路IO复用机制(如select、poll和epoll)进行IO状态阻塞监听。有client1客户端 connect 请求, IO复用机制检测到lfd触发事件读写,则进行accept建立连接,并将新生成的cfd1加入到监听IO集合中。

  • ②client1 再次进行正常读写业务请求,主线程的多路IO复用机制阻塞返回,主线程与client1进行读写通信业务。等到读写业务结束后,会再次返回多路IO复用的地方进行阻塞监听。

  • ③如果client1正在进行读写业务时,server依然在主线程执行流程中继续执行,此时如果有新的客户端申请连接请求,server将没有办法及时响应(因为是单线程,server正在读写),将会把这些还没来得及响应的请求加入阻塞队列中。

  • ④等到server处理完一个客户端连接的读写操作时,继续回到多路IO复用机制处阻塞,其他的连接如果再发送连接请求过来的话,会继续重复②③流程。

 4、模型四:单线程多路IO复用 + 多线程业务工作池

模型分析

前两步跟模型三一致

  • ①主线程main thread 创建 lfd之后,采用多路IO复用机制(如select、poll和epoll)进行IO状态阻塞监听。有client1客户端 connect 请求, IO复用机制检测到lfd触发事件读写,则进行accept建立连接,并将新生成的cfd1加入到监听IO集合中。

  • ②当cfd1有可读消息,触发读事件,并且进行读写消息。

  • ③主线程按照固定的协议读取消息,并且交给worker pool工作线程池,工作线程池在server启动之前就已经开启固定数量的线程,里面的线程只处理消息业务,不进行套接字读写操作。

  • ④工作池处理完业务,触发cfd1写事件,将要回发客户端的数据消息通过主线程写回给客户端

优点
  • 相比于模型三而言,设计了一个worker pool业务线程池,将业务处理部分从主线程抽离出来,为主线程分担了业务处理的工作,减少了因为单线程的串行执行业务机制,多客户端对server的大量请求造成排队延迟的时间。就是说主线程读完数据之后马上就丢给了线程池去处理,然后马上回到多路IO复用的阻塞监听状态。缩短了其他客户端的等待连接时间。

  • 由于是单线程,实际上读写的业务并发还是为1,但是业务流程的并发数为worker pool线程池里的线程数量,加快了业务处理并行效率。

区别

        模型三是客户端向server发起请求时需要排队,模型四是业务处理完之后回写客户端需要排队。

        模型三跟模型四的总体并发效率差不多,因为还是一个线程进行读写。但是对于客户端的体验来说,会觉得响应速度变快,减少了在服务器的排队时间。如果客户端数量不多,并且各个客户端的逻辑业务有并行需求的话适合用该模型。

5、模型五:单线程多路IO复用 + 多线程多路IO复用(线程池)(实际中最常用)

模型分析
  • ①server在启动监听之前,需要创建固定数量N的线程,作为thread pool线程池。

  • ②主线程创建lfd之后,采用多路IO复用机制(如select、epoll)进行IO状态阻塞监听。有client1客户端 connect请求,IO复用机制检测到lfd触发读事件,则进行accept建立连接,并且将新创建的cfd1分发给thread pool线程池中的某个线程监听。

  • ③thread pool中的每个thread都启动多路IO复用机制,用来监听主线程建立成功并且分发下来的socket套接字(cfd)。

  • ④如图,thread1监听cfd1、cfd2,thread2监听cfd3,thread3监听cfd4。线程池里的每一个线程相当于它们所监听的客户端所对应的服务端。当对应的cfd有读写事件时,对应的线程池里的thread会处理相应的读写业务。

优点
  • 将主线程的单流程读写,分散到线程池完成,这样增加了同一时刻的读写并行通道,并行通道数量等于线程池的thread数量N;

  • server同时监听cfd套接字数量几乎成倍增大,之前的全部监控数量取决于主线程的多路IO复用机制的最大限制(select默认1024,epoll默认与内存有关,约3~6w不等)。所以该模型的理论单点server最高的响应并发数量为N*(3 ~ 6w)。(N为线程池thread的数量,建议与cpu核心数一致)

  • 如果良好的线程池数量和CPU核心数适配,那么可以尝试CPU核心与thread绑定,从而降低cpu的切换频率,提高了每个thread处理业务的效率。

缺点
  • 虽然监听的并发数量提升,但是最高读写并行通道依然为N,而且多个身处被同一个thread所监听的客户端也会出现延迟读写现象。实际上线程池里每个thread对应客户端的部分,相当于模型三。

6、模型六:(多进程版)单线程多路IO复用 + 多进程多路IO复用(进程池)

模型分析

与线程池版没有太大的差异。需要在服务器启动之前先创建一些守护进程在后台运行。

存在的不同之处:

  • ①进程间资源不共享,而线程是共享资源的。进程和线程的内存布局不同导致主进程不再进行accept操作,而是将accept过程分散到每一个子进程中

  • ②进程的资源独立,所以主进程如果accept成功cfd,其他的进程是没有办法共享资源的,因此需要各子进程自行accpet创建连接

  • ③主进程只是监听listenFd状态,一旦触发读事件或者有新连接请求,通过IPC进程间通信(signal、mmap、fifo等方式)让所有的子进程们进行竞争,抢到lfd读事件资源的子进程会进行accpet操作,监听他们自己所创建出来的套接字cfd。(自己创建的cfd,由自己监听cfd的读写事件)

猜你喜欢

转载自blog.csdn.net/qq_41834692/article/details/132561033