常见的linux服务器类型
1.多进程并发服务器
2.多线程并发服务器
3.select多路I/O转接服务器
4.poll多路I/O转接服务器
5.epoll多路I/O转接服务器
多路IO是指单个线程通过记录跟踪每一个 IO流的状态同时来管理多个IO流,尽量提高服务器的吞吐量
与多进程多线程相比IO多路复用的计数最大优势就是系统开销小,不必创建进程/线程,也不必维护 进程/线程
IO多路复用使用场景
(1)当客户处理多个描述符时(一般是交互式输入或网络套接口)
(2)当一个客户同时处理多个套接口时
(3)如果一个TCP服务器即要处理监听套接口又要处理UDP一般使用IO复用
(4)如果一个服务器要处理多个服务或协议
select,poll,epoll都是多路复用的机制。所谓的多路复用机制就是通过一种机制可以监视多个文件描述符,一旦某个文件描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作,但是select,poll,epoll本质上都是同步IOd因为他们都需要在读写事件就绪后自己负责进行读写,也就是说读写过程是阻塞的,而异步IO无需自己负责读写,异步IO的实现会负责把数据从内核拷贝到用户控件
1.函数参数
int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* execptfds, struct timeval* timmeout);
nfds:监控的文件描述符集里最大的文件描述符加1
readfds:监控有读数据到达文件描述符集合,传入传出参数
writefds:监控有写数据到达文件描述符集合,传入传出参数
execptfds:监控异常发生文件描述符集合
timeout:设置阻塞监控呢时间
NULL:永远等下去
timeval:等待固定的时间
timeval里时间均为 0,检查描述符字后立即返回,轮询
void FD_ZERO(fd_set* set);把文件描述符集合里初始化0
void FD_SET(int fd,fd_set* set);把文件描述符里fd位置 置1
void FD_CLR(int fd,fd_set* set);把文件描述 符里fd位置 置0
int FD_ISSET(int fd,fd_set* set);测试文件描述符集合里的fd是否为1,有1返回1,没1返回0
2.函数返回值
执行成功返回文件描述词状态已改变的个数
如果返回 0表示词状态改变前已经超过指定 时间,没有返回
当发生错误时返回-1,错误原因可能是errno,此时参数readfds,writefds,exceptfds和timeout的值变得不可预测
错误的值可能为
EBADF;文件描述词无效或该文件已经关闭
EINTR:此调用被信号中断
EINVAL:参数为负值
ENOMEN:核心内存不足
3.函数模型
理解select模型的关键在于理解fd_set,通常fd_set长度为1字节,fd_set中每一个bit可以对应一个文件描述符fd,则一字节长的fd_set可以对应8个fd
(1)执行fd_set set:FD_ZERO(&set)则set用位表示为0000 0000
(2)若fd=5,执行FD_SET(fd,&set)后set变为 0001,0000(第5位置1)
(3)若再加 fd=2,fd=1则set会变为0001,0011
(4)执行select(6,&set,0,0,0)阻塞等待
(5)执行fd=1,fd=2都发生 可读事件则select返回,此时 set变为0000,0011没有事件发生的fd=5被清空
基于以上特点有以下总结:
(1)可控文件描述符的个数取决于sizeof(fd_set)的值
(2)将fd加入select监控集时还要再使用一个数组保存放到监控集中的fd,一是用于返回后arr作为源数据和fd_set进行判断,二是select返回后会把以前加入的但并无事件发生的fd清空,每次开始select之前都要进行重新从arr取得Fd逐一加入,扫描arr的同时取得fd罪的的maxfd,用于作为select的第一个参数
(3)所以select模型 必须在 select前循环arr(加fd,取maxfd),select返回后循环arr(FD_ISSET判断是否有事件发生)
4.select的缺点
(1)每次调用都需要把fd从用户态拷贝到内核态,这个开销在fd很多时会很大
(2)每次调用select都需要在内核遍历传进来的所有fd,这个开销也很大
(3)select支持的文件描述符太小 ,默认 只有1024
(4)select会修改函数传入的参数数组,当这个函数 被调用很多次的时候就会发生错误
(5)
实现过程
(1)创建监听套接字
(2)设置端口复用 127.0.0.1
指定族:IPv4
指定端口号并转化为网络字节序列
指定Ip并转化为网络字节序列
(3)绑定到监听套接字
(4)将客户端数组 全部初始化为 -1
(5)监听描述集初始化,并将监听套接字加到监听集
(6)如果客户端有链接 请求,则将链接请求添加到监听集合里,然后判断下标i的是否大于当前文件描述符,每次都求取最大的文件描述符 ,一次轮询,当数组 满了的时候关闭当前客户端请求