Skip to content

网络编程

网络编程

1、常见的IO模型:

对于一次IO访问(以read举例),数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。所以说,当一个read操作发生时,它会经历两个阶段:

  • 等待数据准备就绪 (Waiting for the data to be ready)
  • 将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)

正式因为这两个阶段,linux系统产生了下面五种网络模式的方案:

  • 阻塞式IO模型(blocking IO model)
  • 非阻塞式IO模型(noblocking IO model)
  • IO复用式IO模型(IO multiplexing model)
  • 信号驱动式IO模型(signal-driven IO model)
  • 异步IO式IO模型(asynchronous IO model)

其中,IO多路复用模型指的是:使用单个进程同时处理多个网络连接IO,他的原理就是select、poll、epoll 不断轮询所负责的所有 socket,当某个socket有数据到达了,就通知用户进程。该模型的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。

2、IO多路复用?

IO多路复用解决了什么问题

​ 一个服务器进程和一个客户端进程通信,服务器端read(sockfd1,bud,bufsize),此时客户端进程没有发送数据,那么read(阻塞调用)将阻塞,直到客户端调用write(sockfd,but,size)发来数据,在一个客户和服务器通信时这没什么问题。

​ 当多个客户与服务器通信时当多个客户与服务器通信时,若服务器阻塞于其中一个客户sockfd1,当另一个客户的数据到达套接字sockfd2时,服务器不能处理,仍然阻塞在read(sockfd1,...)上。

IO多路复用

​ 同时监听多个客户连接,socket1、socket2...当其中有一个发来消息就从select阻塞中返回,然后调用read读取,再回到select循环,这样就不会阻塞在其中一个上而不能处理其他客户消息。该模型的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。

IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程。IO多路复用适用如下场合

  • 当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/O复用。
  • 当一个客户同时处理多个套接口时,而这种情况是可能的,但很少出现。
  • 如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O复用。
  • 如果一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用。
  • 如果一个服务器要处理多个服务或多个协议,一般要使用I/O复用。
  • 与多进程和多线程技术相比,I/O多路复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销。

3、select、poll 和 epoll 之间的区别?

(1)select:时间复杂度 O(n)

select 仅仅知道有 I/O 事件发生,但并不知道是哪几个流,所以只能无差别轮询所有流,找出能读出数据或者写入数据的流,并对其进行操作。所以 select 具有 O(n) 的无差别轮询复杂度,同时处理的流越多,无差别轮询时间就越长。

(2)poll:时间复杂度 O(n)

poll 本质上和 select 没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个 fd 对应的设备状态, 但是它没有最大连接数的限制,原因是它是基于链表来存储的。

(3)epoll:时间复杂度 O(1)

epoll 可以理解为 event poll,不同于忙轮询和无差别轮询,epoll 会把哪个流发生了怎样的 I/O 事件通知我们。所以说 epoll 实际上是事件驱动(每个事件关联上 fd)的。

select,poll,epoll 都是 IO 多路复用的机制。I/O 多路复用就是通过一种机制监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),就通知程序进行相应的读写操作。但 select,poll,epoll 本质上都是同步 I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步 I/O 则无需自己负责进行读写,异步 I/O 的实现会负责把数据从内核拷贝到用户空间。

4、reactor和preactor的IO复用模式

两种高性能IO复用设计模式:Reactor和Proactor。

Reactor模式实现非常简单,使用同步IO模型,即业务线程处理数据需要主动等待或询问,主要特点是利用epoll监听listen描述符是否有相应,及时将客户连接信息放于一个队列,epoll和队列都是在主进程/线程中,由子进程/线程来接管各个描述符,对描述符进行下一步操作,包括connect和数据读写。主程读写就绪事件。

Preactor模式完全将IO处理和业务分离,使用异步IO模型,即内核完成数据处理后主动通知给应用处理,主进程/线程不仅要完成listen任务,还需要完成内核数据缓冲区的映射,直接将数据buff传递给业务线程,业务线程只需要处理业务逻辑即可。

对IO多路复用作了一层封装,让使用者不用考虑底层网络API的细节,关心应用代码的编写。

反应堆模式,对事件进行反应,也就是来了一个事件。Reactor就有相对应的反应/响应。IO多路复用监听事件,收到事件后,根据事件类型分配给某个进程/线程。

Reactor模式主要由Reactor和处理资源池两个核心部分组成,负责的事情如下:

  • Reactor负责监听和分发事件,事件类型包含连接事件、读写事件;
  • 处理资源池负责处理事件,如read—业务逻辑—send;

Reactor模式是灵活多变的,可以应对不同的业务场景:Reactor可以有一个或者多个,处理资源池可以是单个进程/线程,也可以是多个进程/线程。

image-20210913133202913

2、reactor跟proactor的区别?

Reactor

要求主线程,只负责监听文件描述上是否有事件发生,有的话就立即将该事件通知工作线程。除此之外,主线程不做任何其他实质性的工作。读写数据,接受新的连接,以及处理客户请求均在工作线程中完成。

Proactor

将所有I/O操作都交给主线程和内核来处理,工作线程仅负责业务逻辑。

6、负载均衡算法有哪些?

多台服务器以对称的方式组成一个服务器集合,每台服务器都具有等价的地位,能互相分担负载。

  • 轮询法:将请求按照顺序轮流的分配到服务器上。大锅饭,不能发挥某些高性能服务器的优势。
  • 随机法:随机获取一台,和轮询类似。
  • 哈希法:通过ip地址哈希化来确定要选择的服务器编号。好处是,每次客户端访问的服务器都是同一个服务器,能很好地利用session或者cookie。
  • 加权轮询:根据服务器性能不同加权。