基于I/O 多路复用技术的并发服务器

时间:2021-11-25 17:55:03
在实际的应用中,要求一个服务器能同时处理大量的客户请求,所有这些客户将访问绑
定在某一个特定套接字地址上的服务器。因此,服务器必须满足并发的需求。如果不采用并
发技术,当服务器处理一个客户请求时,会拒绝其他客户端请求,造成其他客户要不断的请
求并长期等待。
在Linux(Unix)系统中并发服务器有三种设计方式:
(1)多进程
进程是执行中的计算机程序,可以认为是一个程序的一次运行。它是一个动态的实体,
是独立的任务。每个单独的进程运行在自己的虚拟地址空间中,并且它只能通过安全的内核
管理机制和其它进程交互。若是一个进程崩溃不会引起其它进程崩溃。
在Linux(Unix)系统中,多个进程可以同时执行相同的代码,从而支持并发。
对于单个CPU 系统而言,CPU 一次只能执行一个进程,但操作系统可通过分时处理,
每个进程在每个时间段中执行,因此对于用户而言,这些进程在同时执行。
(2)多线程
线程与进程类似,也支持并发执行。与进程不同的一点,在同一进程中所有线程共享
相同的全程变量以及系统分配给进程的资源。因此,线程占用较少的系统资源,并且线程之
间切换更快。
(3)I/O 多路复用(select 和poll 函数)
另一种支持并发的方法是I/O 多路复用。select()函数是系统提供的,它可以在多个描
述符中选择被激活的描述符进行操作。
例如:一个进程中有多个客户连接,即存在多个TCP 套接字描述符。select()函数阻塞
直到任何一个描述符被激活,即有数据传输。从而避免了进程为等待一个已连接上的数据而
无法处理其他连接。因而,这是一个时分复用的方法,从用户角度而言,它实现了一个进程
或线程中的并发处理。
I/O 多路复用技术的最大优势是系统开销小,系统不必创建进程、线程,也不必维护这
些进程/线程,从而大大减少了系统的开销。
select()函数用于实现I/O 多路复用,它允许进程指示系统内核等待多个事件中的任何一
个发生,并仅在一个或多个事情发送或经过某指定的时间后才唤醒进程。
它的原型如下,
#include<sys/time.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set * errorfds, struct timeval *timeout);
ndfs: select() 函数监视描述符数的最大值。根据进程中打开的描述符数而定,一般设为要
监视的描述符的最大数加1。
readfds: select() 函数监视的可读描述符集合。
writefds: select()函数监视的可写描述符集合。
errorfds: select()函数监视的异常描述符集合。
timeout: select()函数超时结束时间
返回值。如果成功返回总的位数,这些位对应已准备好的描述符。否则返回-1,并在errno
中设置相应的错误码。
FD_ZERO(fd_set *fdset):清空fdset 与所有描述符的联系
FD_SET(int fd, fd_set *fdset):建立描述符fd 与fdset 的联系
FD_CLR(int fd, fd_set *fdset):撤销描述符fd 与fdset 的联系
FD_ISSET(int fd,fd_set *fdset) ::检查与fdset 联系的描述符fd 是否可读写,返回非0表示可读写。
采用select()函数实现I/O 多路复用的基本步骤如下:
(1) 清空描述符集合
(2) 建立需要监视的描述符与描述符集合的联系
(3) 调用select()函数
(4) 检查所有需要监视的描述符,利用FD_ISSET 判断是否准备好
(5) 对已准备好的描述符进行I/O 操作