Network
read/write
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| read函数 ssize_t read(int fd, void *buf, size_t count); 参数: fd:文件描述符 buf:存数据的缓冲区 count:缓冲区大小 返回值: 0: 读到文件末尾。 (对端已经关闭)【 !重 !点 !】 成功; > 0 读到的字节数。 失败: -1, 设置 errno errno = EAGAIN or EWOULDBLOCK: 设置了非阻塞方式读。 没有数据到达 没有读到数据。 说明不是read失败。
errno = EINTR 慢速系统调用被 中断。
errno = “其他情况” 异常。
write函数 ssize_t write(int fd, const void *buf, size_t count); 参数: fd:文件描述符 buf:待写出数据的缓冲区 count:数据大小 返回值: 成功:写入的字节数。 失败:-1, 设置 errno
|
select
man select : linux上查看使用
- 优点
- 缺点
- 监听上限1024
- 每次需要遍历fd_set,找到有读事件的文件并处理
- 需要自己构建一个监听的文件描述符的集合数组来优化这个问题
- 每次select都是系统调用,都要切换用户态和内核态,fd_set要不在用户态和内核态直接反复拷贝
- 参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
nfds:监听的所有文件描述符中,最大文件描述符+1
readfds: 读 文件描述符监听集合。 传入、传出参数
writefds:写 文件描述符监听集合。 传入、传出参数 NULL
exceptfds:异常 文件描述符监听集合 传入、传出参数 NULL
timeout: > 0: 设置监听超时时长。
NULL: 阻塞监听 (timeout)
0: 非阻塞监听,轮询 (timeout) 返回值:
> 0: 所有监听集合(3个)中, 满足对应事件的总数。
0: 没有满足监听条件的文件描述符
-1: errno
|
1 2 3 4 5 6 7
| int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
void FD_CLR(int fd, fd_set *set); int FD_ISSET(int fd, fd_set *set); void FD_SET(int fd, fd_set *set); void FD_ZERO(fd_set *set);
|
poll
优点:
- 自带数组结构。 可以将 监听事件集合 和 返回事件集合 分离。
- 拓展 监听上限。 超出 1024限制。
缺点:
- 不能跨平台。 Linux
- 无法直接定位满足监听事件的文件描述符, 编码难度较大。
参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| int poll(struct pollfd *fds, nfds_t nfds, int timeout);
fds:监听的文件描述符【数组】
struct pollfd { int fd: 待监听的文件描述符 short events: 待监听的文件描述符对应的监听事件
取值:POLLIN、POLLOUT、POLLERR
short revnets: 传入时, 给0。如果满足对应事件的话, 返回 非0 --> POLLIN、POLLOUT、POLLERR }
nfds: 监听数组的,实际有效监听个数。
timeout: > 0: 超时时长。单位:毫秒。
-1: 阻塞等待
0: 不阻塞
返回值:返回满足对应监听事件的文件描述符 总个数。
|
epoll
epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| int epoll_create(int size);
size:创建的红黑树的监听节点数量。(仅供内核参考。)
返回值:指向新创建的红黑树的根节点的 fd。
失败: -1 errno
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epfd:epoll_create 函数的返回值。 epfd
op:对该监听红黑数所做的操作。
EPOLL_CTL_ADD 添加fd到 监听红黑树
EPOLL_CTL_MOD 修改fd在 监听红黑树上的监听事件。
EPOLL_CTL_DEL 将一个fd 从监听红黑树上摘下(取消监听)
fd: 待监听的fd
event: 本质 struct epoll_event 结构体 地址
成员 events:
EPOLLIN / EPOLLOUT / EPOLLERR
成员 data: 联合体(共用体):
int fd; 对应监听事件的 fd
void *ptr;
uint32_t u32;
uint64_t u64;
返回值:成功 0; 失败: -1 errno int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
epfd:epoll_create 函数的返回值。 epfd
events:传出参数,【数组】, 满足监听条件的 那些 fd 结构体。
maxevents:数组 元素的总个数。 1024 struct epoll_event evnets[1024] timeout:
-1: 阻塞
0: 不阻塞
>0: 超时时间 (毫秒)
返回值:
> 0: 满足监听的 总个数。 可以用作循环上限。
0: 没有fd满足监听事件
-1:失败。 errno
|
ET/LT
Epoll的边缘触发ET为什么要搭配非阻塞I/O使用?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| event.events = EPOLLIN | EPOLLET;
epoll 的 ET模式, 高效模式,但是只支持 非阻塞模式。 --- 忙轮询。 ????为什么只支持非阻塞????
struct epoll_event event;
event.events = EPOLLIN | EPOLLET;
epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &event); int flg = fcntl(cfd, F_GETFL);
flg |= O_NONBLOCK;
fcntl(cfd, F_SETFL, flg);
|
epoll反应堆
day4-4-源代码-epoll_ET_LT
/learn/network/tcp_epoll/libevent.c
epoll ET模式 + 非阻塞、轮询 + void *ptr
相关操作
1
| nc 127.1 8000 // 直接和这个ip+port建立连接
|
函数跳转包安装
ctags视频教程