水平触发 LT
边沿触发 ET
因为设置为水平触发,只要缓存区有数据epoll_wait就会被触发,epoll_wait是一个系统调用,尽量少调用
所以尽量使用边沿触发,边沿出触发数据来一次只触发一次,这个时候要求一次性将数据读完,所以while循环读,读到最后read默认带阻塞,不能让read阻塞,因为不能再去监听,
设置cfd为非阻塞,read读到最后一次返回值为-1.判断errno的值为EAGAIN,代表数据读干净
工作中 边沿触发 + 非阻塞 = 高速模式
创建lfd,监听,创建树epfd,写epoll_event事件,事件是结构体,ev.data.fd,ev.events(EPOLLIN 或者EPOLLOUT),epoll_ctl对文件描述符对上树(EPOLL_CTL_ADD)或者下树(EPOLL_CTL_DEV),epoll_wait监听,返回值是变化的数量,先判断lfd变化的操作,是创建新的cfd,然后再将其上树,注意设置非阻塞,再判断cfd变化的操作,要设置一个标志位,一次性读完数据,别忘记关闭文件描述符和下树。
文件描述符 事件 回调函数 封装在一起
结构体来封装
反应堆复杂版代码
反应堆多了回调函数,是结构体,将文件描述符,事件,回调函数封装一起,指针的形式
3.线程池
线程池,事先创建几个线程,,不停取任务,如果没有任务休眠,省去了不停的创建线程销毁销毁线程的事件和资源
注意: 线程池 处理的任务,所需要处理的时间必须很短
一个锁
两个条件变量
循环队列
TCP: 传输控制协议 安全可靠 丢包重传 面向连接(电话模型)
UDP: 用户数据报协议 不安全不可靠 丢包不重传 快 不面向连接(邮件模型)
tcp通信流程:
服务器: 创建流式套接字 绑定 监听 提取 读写 关闭
客户端: 创建流式套接字 连接 读写 关闭
收发数据:
read recv
ssize_t recv(int sockfd, void *buf, size_t len, int flags);//flags==MSG_PEEK 读数据不会删除缓冲区的数据
write send
ssize_t send(int sockfd, const void *buf, size_t len, int flags);//flags=1 紧急数据
udp通信流程
服务器: 创建报式套接字 绑定 读写 关闭
客户端: 创建报式套接字 读写 关闭
发数据:
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
dest_addr: 目的地的地址信息
addrlen: 结构体大小
收数据:
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
src_addr: 对方的地址信息
addrlen: 结构体大小的地址
socket
int socket(int domain, int type, int protocol);
参数:
domain : AF_INET
type :SOCK_DGRAM
protocol :0
unix domain socket
本地套接字通信
全双工
套接字用文件来标识,这个文件在绑定之前是不能存在
创建本地套接字
绑定
监听
提取
读写
关闭
int socket(int domain, int type, int protocol);
参数:
domain : AF_UNIX
type :SOCK_STREAM
protocol : 0
绑定
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
sockfd: 本地套接字
addr: 本地套接字结构体地址
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX */
char sun_path[108]; /* pathname *///文件的路径名
};
addrlen: sockaddr_un大小
提取
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
addr: struct sockaddr_un 结构体地址
需要注意的点:
客户端可以隐式绑定,但是服务器不可以
绑定指定文件时m,这个文件必须不存在,如果存在绑定失败
网络的事件库
跨平台,支持多种多路io复用
核心: 以事件驱动(触发),回调
安装libevent
解压:
tar -xzvf libevent-2.0.22-stable.tar.gz -C ./install
执行configure ,检测环境生成makefile
./configure
编译
sudo make
安装
sudo make install
库的路径: /usr/local/lib
头文件目录: /usr/local/include'
编译时需要指定库名 -levent
创建event_base根节点
struct event_base *event_base_new(void);
返回值值就是event_base根节点地址
释放根节点
void event_base_free(struct event_base *);
循环监听
int event_base_dispatch(struct event_base *base);
相当于 while(1){epoll_wait}循环监听
退出循环监听
int event_base_loopexit(struct event_base *base, const struct timeval *tv); //等待固定时间之后退出
int event_base_loopbreak(struct event_base *base);//立即退出
初始化上树节点
struct event *event_new(struct event_base *base, evutil_socket_t fd, short events, event_callback_fn cb, void *arg);
参数:
base: event_base根节点
fd: 上树的文件描述符
events: 监听的事件
#define EV_TIMEOUT 0x01 //超时事件
#define EV_READ 0x02 //读事件
#define EV_WRITE 0x04 //写事件
#define EV_SIGNAL 0x08 //信号事件
#define EV_PERSIST 0x10 //周期性触发
#define EV_ET
cb: 回调函数
typedef void (*event_callback_fn)(evutil_socket_t fd, short events, void *arg);
arg: 传给回调函数的参数
返回值: 初始化好的节点的地址
节点上树
int event_add(struct event *ev, const struct timeval *timeout);
ev: 上树节点的地址
timeout: NULL 永久监听 固定时间 限时等待
下树
int event_del(struct event *ev);
ev: 下树节点的地址
释放节点
void event_free(struct event *ev);
epoll工作模式:水平触发,边沿触发,水平触发在读缓冲区,只要有数据就触发,边沿触发,在读缓冲区,来一次数据触发一次(一次性读完数据),写缓冲区,水平触发只要可写就触发,边沿触发有数据变化就触发,创建lfd,监听,创建树epfd,写epoll_event事件,事件是结构体,ev.data.fd,ev.events(EPOLLIN 或者EPOLLOUT),epoll_ctl对文件描述符对上树(EPOLL_CTL_ADD)或者下树(EPOLL_CTL_DEV),epoll_wait监听,返回值是变化的数量,先判断lfd变化的操作,是创建新的cfd,然后再将其上树,注意设置非阻塞,再判断cfd变化的操作,要设置一个标志位,一次性读完数据,别忘记关闭文件描述符和下树。
epollreactor模式使用的是结构体,有事件,文件描述符,回调函数,指针形式
tcp传输控制协议,安全可靠,丢包重传,面向连接,udp用户数据报协议,与tcp相反,tcp是流式套接字,udp是报式套接字,要注意两者客户端服务端的区别,
创建udp套接字,注意是SOCK_DGRAM,
创建本地套接字,就是与tcp相比缺少了连接步骤,
本地套接字需要自定一个路径,生成文件,最后执行文件,如果之前存在文件,需要删除在运行
libevent网路事件库,支持多路io复用,以事件驱动触发事件回调,
libevent使用,event_base_new创建新节点,event_base_free释放节点,event_base_dispatch循环监听
libevent流程event_new新建节点,节点上树event_add监听,event_del下树,event_free节点释放