TCP并发服务器:多进程 多线程 IO多路复用(select poll epoll)

TCP并发服务器

单循环服务器:一次只能处理一个客户端任务的服务器。

并发服务器:能够同时处理多个客户端任务的服务器。(一对多)

  • UDP服务端:具备并发性能
  • TCP服务端:建立连接,单循环服务器

TCP并发服务端四种实现方案

  1. 多进程 安全、资源开销大、并发量小

  2. 多线程 相对进程资源开销小,相对并发量大

  3. 线程池 多线程模型。提前创建好大量线程管理起来,避免反复创建线程带来时间消耗

    生产者--消费者 设计模式 主线程负责生产,线程池负责执行任务 任务队列

  4. IO多路复用 在不创建新进程/线程的前提下,使用一个进程同时监测多路IO

    文件 ---》fd 对多个文件描述符的监测,复用一个进程。 实现接口:selectpollEpoll

多进程伪代码

复制代码
Socket();
Bind();
Listen();
While(1)
{
    Int connfd=accept();
    Pid_t pid=fork();
    If(pid>0)
    {

    }
    Else if(pid==0)
    {
        While(1)
        {
            Recv();
            Send();
        }
    }
}

多线程伪代码

复制代码
Socket();
Bind();
Listen();
While(1)
{
    Int  connfd=accept();
    Pthread_create();
    Pthread_datach();//分离属性
}

IO多路复用

在一个进程中,同时监测多个IO

select、poll、epoll

select

1)创建文件描述符集合(位图(数组)----》1024)

复制代码
fd_set fds;
FD_ZERO(&fds);

2)将要监测的文件描述符加入集合

复制代码
FD_SET(fd,&fds);

3)将集合传递给内核开始监测IO事件,并返回监测的结果

复制代码
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
select函数
复制代码
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

功能:通知内核监测文件描述符中的事件,并返回检测到的结果

参数

  • nfds :集合中最大的文件描述符+1
  • readfds:读事件的文件描述符集合
  • writefds:写事件的文件描述符集合
  • exceptfds:出错事件的文件描述符集合
  • timeout:超时时间
    • NULL :不设置超时时间(阻塞等待)

返回值

  • 成功:返回到达IO事件的个数
  • 失败:-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);
//功能:将集合表全部清零
select特点
  1. 使用数组(位图)保存文件描述符集合,最多允许监测1024个文件描述符;
  2. 将集合表创建在应用层,需要应用层和内核层的反复数据拷贝;
  3. 需要遍历寻找到达的IO事件,效率低;
  4. 只能工作在水平触发模式(低速模式),不能工作在边沿触发模式(高速模式);
poll特点
  1. 使用链表保存文件描述符集合,允许监测的文件描述符没有上限限制;
  2. 将集合表创建在应用层,需要应用层和内核层的反复数据拷贝;
  3. 需要遍历寻找到达的IO事件,效率低;
  4. 只能工作在水平触发模式(低速模式),不能工作在边沿触发模式(高速模式);
epoll特点
  1. 使用树形结构保存文件描述符集合(红黑树),允许监测的文件描述符没有上限限制,并且查找效率高;
  2. 将集合表直接创建在内核层,避免应用层和内核层的反复数据拷贝;
  3. 直接返回到达的事件,不需要应用层遍历查找;
  4. 可以工作在水平触发模式(低速模式),也可以工作在边沿触发模式(高速模式);

epoll使用步骤

  1. 创建文件描述符集合
  2. 添加文件描述符到集合
  3. 通知内核开始监测事件
epoll_create
复制代码
int epoll_create(int size);

功能 :创建文件描述符集合 参数

  • size:监测的文件描述符的最大上限 返回值
  • 成功:返回集合代表的文件描述符(句柄)
  • 失败:-1
epoll_ctl
复制代码
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

功能 :对epoll的文件描述符集合进行操作 参数

  • epfd:文件描述符集合
  • op:操作指令
    • EPOLL_CTL_ADD:添加
    • EPOLL_CTL_MOD:修改
    • EPOLL_CTL_DEL:删除
  • fd:需要操作的文件描述符
  • event:事件类型 返回值
  • 成功:0
  • 失败:-1
复制代码
typedef union epoll_data {
void *ptr;
int fd;  //要监测的文件描述符
uint32_t u32;
uint64_t u64;
} epoll_data_t;

struct epoll_event {
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};

events:事件类型

  • EPOLLIN:读事件
  • EPOLLOUT:写事件
epoll_wait
复制代码
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

功能 :通知内核开始监测文件描述符集合并等待返回监测到的事件结果 参数

  • epfd:文件描述符集合
  • events:保存到达事件的集合
  • maxevents:监测的事件最大个数
  • timeout :超时时间
    • -1 :不设置超时,一直阻塞等待

返回值

  • 成功:返回到达事件的个数
  • 失败:-1
  • 0:超时时间到达,却没有事件返回

IO多路复用的应用场景

相关推荐
kruptos44 分钟前
嵌入式 Linux 驱动开发:第一个内核模块怎么写?
linux·运维·驱动开发·嵌入式硬件
wangqiaowq1 小时前
langfuse linux下的安装部署
linux·运维·服务器
NeilYuen1 小时前
用RDMA实现文件传输
linux
Discipline~Hai1 小时前
Linux网络编程05-sqlite3数据库
linux·c语言·网络·数据库·sqlite·linux应用软件编程
Huangjin007_1 小时前
【Linux 系统篇(十七)】进程(五) :进程切换、O(1) 调度算法
linux·运维·服务器
weixin_464307631 小时前
linux虚拟机断网
linux·运维·服务器
λqaq71 小时前
Docker 容器入门基础:Cloud Studio 在线 Linux 环境上手
linux·运维·docker
en.en..2 小时前
LVGL 普通函数指针与结构体函数指针原理
linux·windows·microsoft
byte轻骑兵2 小时前
【BlueZ 】Adapter 模块:蓝牙适配器(主设备)的初始化与核心逻辑
linux·人工智能·bluez·电脑蓝牙·嵌入式蓝牙