一、TCP并发服务器问题

服务端需要连接多个客户端时,服务器需要使用accept等待三次握手连接,同时还需要recv等待接受所有客户端发送的数据,由于accept和recv都是阻塞IO,所以导致程序逻辑无法编写
  解决方法:
        1、TCP并发服务器线程/进程模型:
        优点:实现简单
        缺点:资源消耗大,每个TCP连接都会分配一个线程任务
        2、TCP并发服务器多路复用模型:

二、Linux系统的4种IO模型
1. 阻塞IO
        效率高,数据没来时,CPU阻塞等待,不占用CPU资源

----使用示例


2. 非阻塞IO
        效率低,需要轮询是否有IO事件发生

----使用示例


3. 异步IO
        当内核监测到有IO事件发生时,主动向应用层上报信号事件

----使用示例


4. 多路复用IO
        (1)用一个函数接口监听多个文件描述符是否产生IO事件,只要其中一个产生事件,则不再阻塞,用户可以处理对应的事件
        select
        poll
        epoll

        (2)区别:
        1. select有4个缺点:
                select监听的文件描述符集合是一个数组,是有上限的(一般为1024)
                select监听的文件描述符集合在应用层,当事件发生需要产生一次内核层向应用层数据的拷贝,会产生资源开销
                select只能工作在水平触发模式(低速模式)
                select必须将所有在文件描述符集合中的元素找一遍才能找到产生事件的文件描述符
        2. poll:
                poll监听的事件集合在链表中,没有上限的限制
                其余与select特点相同(3个缺点)
        3. epoll:
                没有文件描述符上限的限制

                epoll创建一张内核监听的事件表,所以事件表在内核中,无需与应用层交互效率高
                epoll可以工作在水平触发(默认)和边沿触发模式(EPOLLET)
                epoll可以将产生事件的所有文件描述符返回,而不需要手动检测所有文件符是否有事件发生

----使用示例

三、函数接口

1、fcntl

原型:int fcntl(int fd, int cmd, ... /* arg */ );
功能:
        向文件描述符发送cmd命令
参数:
        fd:文件描述符
        cmd:
                F_GETFL 获得属性
                F_SETFL 设置属性
返回值:
        成功返回0
        失败返回-1

2、select

原型:int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
功能:
        监听文件描述符集合中是否有事件发生
参数:
        nfds:最大的文件描述符+1
        readfds:读文件描述符集合
        writefds:写文件描述符集合
        execptfds:其余文件描述符集合
        timeout:超时时间,传NULL表示一直阻塞
返回值:
        成功返回产生事件的文件描述符个数
        失败返回-1
        如果超时仍然没有事件发生返回0

void FD_CLR(int fd, fd_set *set);
功能:
        将文件描述符fd从文件描述符集合中删除
int FD_ISSET(int fd, fd_set *set);
功能:
        判断fd是否仍在文件描述符集合中
void FD_SET(int fd, fd_set *set);
功能:
        将fd加入文件描述符集合中
void FD_ZERO(fd_set *set);
功能:
        将文件描述符集合清0

练习:使用select实现TCP并发服务器

3、poll

原型:int poll(struct pollfd *fds, nfds_t nfds, int timeout);
功能:
        监听事件集合中的事件是否发生
参数:
        fds:监听事件集合的数组首地址
        nfds:事件数组的元素个数
        timeout:超时时间, -1 一直阻塞直到事件发生
返回值:
        失败返回-1
        成功返回产生事件的个数
        时间达到没有事件发生返回0

4、 epoll

int epoll_create(int size);
功能:
        创建一张内核监听的事件表
参数:
        size:事件表的事件的个数
返回值:
        成功返回文件描述符
        失败返回-1

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
功能:
        向内核事件表中添加、修改、删除事件
参数:
        epfd:内核事件表的文件描述符
        op:   EPOLL_CTL_ADD 添加事件
                EPOLL_CTL_MOD 修改事件
                EPOLL_CTL_DEL 删除事件
        fd:操作的文件描述符
        event:事件集合
返回值:
        成功返回0
        失败返回-1
int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
功能:
        监听事件表中的事件
参数:
        epfd:文件描述符
        events:存放产生事件的空间的首地址
        maxevents:数组的大小
        timeout:超时时间
返回值:
        成功返回产生事件的文件描述符个数
        失败返回-1
        时间达到没有事件产生返回0

练习:利用poll实现管道文件和终端的多路复用

利用poll实现TCP并发服务器

利用epoll实现管道文件和终端的多路复用

利用poll实现TCP并发服务器

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐