TCP并发模型:
1.TCP多线程模型:
缺点:
1.创建线程会带来 资源开销 2.能够实现的 并发量 比较有限
2.IO模型:
1.阻塞IO:
没有数据到来时,可以让任务挂起 节省CPU资源开销,提高系统效率
2.非阻塞IO:
程序未接收到数据时一直执行 效率很低
3.异步IO
只能绑定一个文件描述符用来 读取数据
4.多路复用IO
select
1.select监听的集合中的文件描述符有 上限限制
2.select有 内核层 向 用户层数据空间 拷贝 的过程,占用系统资源开销
3.select必须 轮询检测 产生 事件 的文件描述符
4.select 只能工作 在 水平触发 模式(低速模式) 无法工作 在 边沿触发 模式(高速模式)
poll (监听的集合中的文件描述符有 没有上限限制)
1.poll有 内核层 向 用户层 数据空间 拷贝 的过程,占用系统资源开销
2.poll必须 轮询检测 产生 事件 的文件描述符
3.poll 只能工作在水平触发模式(低速模式) 与select相同 无法工作在边沿触发(高速模式)
水平触发:产生事件通知,没处理完成,下次继续通知此事件,阻塞在这里,直到处理完成此事件(低速)。
边沿触发:产生事件的时刻通知,只通知一次,下次通知其它事件,不阻塞。
poll:内部链表结构,所以无上限。
3.函数接口:
1.select
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
功能:
select 监听 文件描述符集合 中 是否 有文件描述编程 ready状态
select 监听 文件描述符集合 中 ,若有状态 , 将没有ready状态的T除 若无状态,将阻塞继续等待 参数:
nfds: 最大文件描述符的值 +1
readfds: 读 文件描述符集合
writefds: 写 文件描述符集合
exceptfds: 其余 文件描述符集合
timeout: 等待的时长
NULL 一直等待(超时处理)
返回值:
成功 返回 文件描述符集合中 的 文件描述符个数
失败 返回 -1
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
2.poll
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
功能:
监听 文件描述符集合 是否 有事件发生
参数:
fds: 监听 文件描述符集合 数组空间首 地址
nfds: 监听 文件描述符集合 元素个数
timeout: 等待的时间 (非0) (-1 一直等待)
返回值:
成功 返回 产生事件的文件描述符 个数
失败 返回 -1
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
fd: 监听的文件描述符
events: 要监听的 事件 POLLIN 是否 可读 POLLOUT 是否 可写
revents: 实际 产生的 事件
3.epoll (回调函数查找)
epoll可工作在水平触发模式和边沿触发模式
#include <sys/epoll.h>
// 创建一个epoll实例
int epfd = epoll_create(1);
// 设置epoll事件
struct epoll_event ev;
ev.events = EPOLLIN | EPOLLET; // EPOLLIN表示监听读事件,EPOLLET表示边缘触发
ev.data.fd = sockfd; // sockfd是需要监听的文件描述符
// 将文件描述符和事件注册到epoll实例中
epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);
在上面的代码中,通过将ev.events
设置为EPOLLIN | EPOLLET
,我们就在边缘触发模式下监听了sockfd
的读事件。如果我们去掉EPOLLET
,那么就是默认的水平触发模式。
在内核中回调函数查找
-
回调机制:虽然
epoll
本身不直接使用用户空间的回调函数,但是内核中的其他部分可能会使用回调。例如,当网络设备接收到数据包时,它会触发一个中断,这个中断会调用一个内核中的回调函数来处理数据包,并更新相关的文件描述符状态。
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:
事件 对应的 事件
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 :表示对应的文件描述符可以读(包括对端SOCKET正常关闭)
EPOLLOUT:表示对应的文件描述符可以写;
返回值:
成功返回0
失败返回-1
epoll_wait
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
功能:
监听 事件表中 的 事件
参数:
epfd: 文件描述符
events: 存放 实际产生事件 的数组空间 首地址
maxevents: 最多 存放事件 的 个数
timeout: 设定监听的时间(超过该时间 则不再监听)
-1 一直监听直到有事件发生
返回值:
成功返回产生事件的文件描述符个数
失败返回-1
如果时间达到 仍没有事件发生 返回0