Linux NIO 系列(04-2) poll
Linux NIO 系列(04-2) poll
Netty 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html)
一、select 和 poll 比较
select() 和 poll() 系统调用的本质一样,管理多个描述符也是进行轮询,根据描述符的状态进行处理,但是 poll() 没有最大文件描述符数量的限制(但是数量过大后性能也是会下降)。poll() 和 select() 同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。
二、poll API
poll()函数介绍
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
(1) 功能:
监视并等待多个文件描述符的属性变化
(2) 参数:
fds:指向一个结构体数组的第 0 个元素的指针,每个数组元素都是一个 struct pollfd 结构,用于指定测试某个给定的 fd 的条件
struct pollfd {
int fd; // 文件描述符
short events; // 等待的事件
short revents; // 实际发生的事件
};
nfds:用来指定第一个参数数组元素个数。
timeout:指定等待的毫秒数,无论 I/O 是否准备好,poll() 都会返回。
(3) pollfd 数据结构
fd:每一个 pollfd 结构体指定了一个被监视的文件描述符,可以传递多个结构体,指示 poll() 监视多个文件描述符。
events:指定监测 fd 的事件(输入、输出、错误),每一个事件有多个取值,如下:
POLLIN 有数据可读
POLLRDNORM 有普通数据可读,等效与POLLIN
POLLPRI 有紧迫数据可读
POLLOUT 写数据不会导致阻塞
POLLER 指定的文件描述符发生错误
POLLHUP 指定的文件描述符挂起事件
POLLNVAL 无效的请求,打不开指定的文件描述符
revents:revents 域是文件描述符的操作结果事件,内核在调用返回时设置这个域。events 域中请求的任何事件都可能在 revents 域中返回。
注意:每个结构体的 events 域是由用户来设置,告诉内核我们感兴趣的事件是什么,而 revents 域是返回时内核设置的,以说明对该描述符发生了什么事件。
(4) 返回值
成功时,poll() 返回结构体中 revents 域不为 0 的文件描述符个数;如果在超时前没有任何事件发生,poll() 返回 0;
失败时,poll() 返回 -1,并设置 errno 为下列值之一:
EBADF: 一个或多个结构体中指定的文件描述符无效。
EFAULT: fds 指针指向的地址超出进程的地址空间。
EINTR: 请求的事件之前产生一个信号,调用可以重新发起。
EINVAL: nfds 参数超出 PLIMIT_NOFILE 值。
ENOMEM: 可用内存不足,无法完成请求。
附1:linux 每个进程IO限制
# 当前计算机所能打开的最大文件个数。受硬件影响,这个值也可以改(通过limits.conf)
cat /proc/sys/fs/file-max
# 查看一个进程可以打开的socket描述符上限。缺省为1024
ulimit -a
# 修改为默认的最大文件个数。【注销用户,使其生效】
ulimit -n 2000
# soft软限制 hard硬限制。所谓软限制是可以用命令的方式修改该上限值,但不能大于硬限制
vi /etc/security/limits.conf
* soft nofile 3000 # 设置默认值。可直接使用命令修改
* hard nofile 20000 # 最大上限值
附2:poll 网络编程
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<ctype.h>
#include<poll.h>
#define SERVER_PORT 8888
#define OPEN_MAX 3000
#define BACKLOG 10
#define BUF_SIZE 1024
int main() {
int i, j, maxi;
int listenfd, connfd, sockfd; // 定义套接字描述符
int nready; // 接受 pool 返回值
int recvbytes; // 接受 recv 返回值
char recv_buf[BUF_SIZE]; // 发送缓冲区
struct pollfd client[OPEN_MAX]; // struct pollfd* fds
// 定义 IPV4 套接口地址结构
struct sockaddr_in seraddr; // server 地址
struct sockaddr_in cliaddr; // client 地址
int cliaddr_len;
// 初始化IPV4套接口地址结构
seraddr.sin_family = AF_INET; // 指定该地址家族
seraddr.sin_port = htons(SERVER_PORT); // 端口
seraddr.sin_addr.s_addr = INADDR_ANY; // IPV4的地址
bzero(&(seraddr.sin_zero), 8);
// socket()函数
if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket error");
exit(1);
}
// 地址重复利用
int on = 1;
if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
perror("setsockopt error");
exit(1);
}
// bind() 函数
if(bind(listenfd, (struct sockaddr *)&seraddr, sizeof(struct sockaddr)) == -1) {
perror("bind error");
exit(1);
}
// listen()函数
if(listen(listenfd, BACKLOG) == -1) {
perror("listen error");
exit(1);
}
client[0].fd = listenfd; // 将 listenfd 加入监听序列
client[0].events = POLLIN; // 监听读事件
// 初始化client[]中剩下的元素
for(i = 1;i < OPEN_MAX;i++) {
client[i].fd = -1; //不能用 0,0 也是文件描述符
}
maxi = 0; //client[]中最大元素下标
while(1) {
nready = poll(client, maxi + 1, -1);//阻塞监听
if(nready < 0) {
perror("poll error!\n");
exit(1);
}
if(client[0].revents & POLLIN) { //位与操作;listenfd的读事件就绪
cliaddr_len = sizeof(cliaddr);
if((connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &cliaddr_len))==-1) {
perror("accept error");
exit(1);
}
printf("client IP: %s\t PORT : %d\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));
//将sockfd加入监听序列
for(i = 1; i < OPEN_MAX; i++) {
if(client[i].fd < 0) {
client[i].fd = connfd;
break;
}
}
if(i == OPEN_MAX) {
perror("too many clients!\n");
exit(1);
}
client[i].events = POLLIN;//监听connfd的读事件
if(i > maxi) {
maxi = i;
}
//判断是否已经处理完事件
if(--nready == 0) {
continue;
}
}
// 检测客户端是否发来消息
for(i = 1; i <= maxi; i++) {
if((sockfd = client[i].fd) < 0) {
continue;
}
if(client[i].revents & POLLIN) {
memset(recv_buf, 0, sizeof(recv_buf));
recvbytes = recv(sockfd, recv_buf, BUF_SIZE, 0);
if(recvbytes < 0) {
// `errno == EINTR` 被异常中断,需要重启。收到 RST 标志
// `errno == EAGIN 或 EWOULDBLOCK` 以非阻塞方式读数据,但没有数据,需要再次读
// `errno == ECONNRESET` 连接被重置,需要 close,移除连接
// `errno == other` 其它异常
if(errno == ECONNRESET) { // RET标志
printf("client[%d] aborted connection!\n",i);
close(sockfd);
client[i].fd = -1;
} else {
perror("recv error!\n");
exit(1);
}
} else if(recvbytes == 0) {
printf("client[%d],close!\n",i);
close(sockfd);
client[i].fd = -1;
} else {
send(sockfd, recv_buf, recvbytes, 0);
}
if(--nready == 0) {
break;
}
}
}
}
return 0;
}
参考:
每天用心记录一点点。内容也许不重要,但习惯很重要!
Linux NIO 系列(04-2) poll的更多相关文章
- Linux NIO 系列(04-4) select、poll、epoll 对比
目录 一.API 对比 1.1 select API 1.2 poll API 1.3 epoll API 二.总结 2.1 支持一个进程打开的 socket 描述符(FD)不受限制(仅受限于操作系统 ...
- Linux NIO 系列(02) 阻塞式 IO
目录 一.环境准备 1.1 代码演示 二.Socket 是什么 2.1 socket 套接字 2.2 套接字描述符 2.3 文件描述符和文件指针的区别 三.基本的 SOCKET 接口函数 3.1 so ...
- Linux NIO 系列(03) 非阻塞式 IO
目录 一.非阻塞式 IO 附:非阻塞式 IO 编程 Linux NIO 系列(03) 非阻塞式 IO Netty 系列目录(https://www.cnblogs.com/binarylei/p/10 ...
- Linux NIO 系列(04-3) epoll
目录 一.why epoll 1.1 select 模型的缺点 1.2 epoll 模型优点 二.epoll API 2.1 epoll_create 2.2 epoll_ctl 2.3 epoll_ ...
- Linux NIO 系列(04-1) select
目录 一.select 机制的优势 二.select API 介绍与使用 2.1 select 2.2 fd_set 集合操作 2.3 select 使用范例 三.深入理解 select 模型: 四. ...
- Java NIO系列教程(七) selector原理 Epoll版的Selector
目录: Reactor(反应堆)和Proactor(前摄器) <I/O模型之三:两种高性能 I/O 设计模式 Reactor 和 Proactor> <[转]第8章 前摄器(Proa ...
- Java 集合系列 04 LinkedList详细介绍(源码解析)和使用示例
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- c/c++ linux epoll系列1 创建epoll
linux epoll系列1 创建epoll 据说select和poll的弱点是,随着连接(socket)的增加,性能会直线下降. epoll不会随着连接(socket)的增加,性能直线下降. 知识点 ...
- (4)top详解 (每周一个linux命令系列)
(4)top详解 (每周一个linux命令系列) linux命令 top详解 引言:今天的命令是用来看cpu信息的top top 我们先看man top top - display Linux pro ...
随机推荐
- 百度网盘不限速-Motrix
目录: Windows 下使用 Motrix Mac 下使用 BaiduNetdiskPlugin-macOS Mac 下使用 Motrix Windows 系统 Motrix 1. Google ...
- MongoDB Windows之ZIP安装
下载.下载地址同MSI下载地址:https://www.mongodb.com/download-center/community .Package选择Zip. 下载完成后解压到对应文件夹,该文件夹就 ...
- 【题解】图的m着色问题
题目背景 给定无向连通图G和m种不同的颜色.用这些颜色为图G的各顶点着色,每个顶点着一种颜色.如果有一种着色法使G中每条边的2个顶点着不同颜色,则称这个图是m可着色的.图的m着色问题是对于给定图G和m ...
- UVA 11355 Cool Points( 极角计算 )
We have a circle of radius R and several line segments situated within the circumference of this cir ...
- 解决“每次打开office2010的word都会出现配置进度框”问题
在win7中安装完office2010后.打开 *.doc文件时,总会弹出"配置进度框"问题,解决例如以下: 1)点击"開始"-->"执 ...
- windows10图形化连接CentOS7
前提:CentOS已经安装图形化,安装教程可以百度 [root@localhost ~]# cat /etc/redhat-release CentOS Linux release (Core) 安装 ...
- 一、spring的基本认识
Spring的认识 Spring是一个开放源代码的设计层面框架,它解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用 Spring是于2003 年兴起的一个轻量级的J ...
- docker镜像仓库
搭建私有镜像仓库 Docker Hub作为Docker默认官方公共镜像,如果想自己搭建私有镜像仓库,官方也提供registry镜像,使得搭建私有仓库非常简单. 下载registry镜像并启动 [roo ...
- getopts的错误报告模式
- go语言从例子开始之Example18.struct结构体
Go 的结构体 是各个字段字段的类型的集合.这在组织数据时非常有用 Example: package main import "fmt" type product struct{ ...