I/O复用--poll
poll
poll(2) - Linux manual page (man7.org)
函数原型
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
poll对于select而言,简单很多。
- pollfd结构体
/* Data structure describing a polling request. */
struct pollfd
{
int fd; /* File descriptor to poll. */
short int events; /* Types of events poller cares about. */
short int revents; /* Types of events that actually occurred. */
};
- fd:要监听的文件描述
- events:要监听的事件
- revents:fd发生的事件
- nfds:指明fds数组大小
typedef unsigned long int nfds_t;
- timeout:指明超时时间,单位为毫秒
The timeout argument specifies the number of milliseconds that poll() should block waiting for a file descriptor to become ready. The call will block until either:
• a file descriptor becomes ready;
• the call is interrupted by a signal handler; or
• the timeout expires.
Note that the timeout interval will be rounded up to the system clock granularity, and kernel scheduling delays mean that the
blocking interval may overrun by a small amount. Specifying a negative value in timeout means an infinite timeout. Specifying
a timeout of zero causes poll() to return immediately, even if no file descriptors are ready.上面指明timeout参数单位为毫秒、poll阻塞和返回的原因以及指定的timeout会根据系统来调整。
图来自:(221条消息) poll函数详解_青季的博客-CSDN博客_poll函数
-
返回值
-
成功时,poll() 返回结构体中 revents 域不为 0 的文件描述符个数;如果在超时前没有任何事件发生,poll()返回 0;
-
失败时,poll() 返回 -1,并设置 errno 为下列值之一:
-
EBADF:一个或多个结构体中指定的文件描述符无效。
-
EFAULT:fds 指针指向的地址超出进程的地址空间。
-
EINTR:请求的事件之前产生一个信号,调用可以重新发起。
-
EINVAL:nfds 参数超出 PLIMIT_NOFILE 值。
-
ENOMEM:可用内存不足,无法完成请求。
-
-
poll事件
这里只摘抄了一些常用的事件,其余参考man手册。
- POLLIN:
- There is data to read.
- 有数据可以读
- POLLOUT:
- Writing is now possible, though a write larger than the available space in a socket or pipe will still block (unless O_NONBLOCK is set).
- 可写
- POLLRDHUP (since Linux 2.6.17):
- Stream socket peer closed connection, or shut down writing half of connection.
- 流socket连接的对端close connection,或者只close了写连接
- POLLERR:
- Error condition (only returned in revents; ignored in events). This bit is also set for a file descriptor referring to the write end of a pipe when the read end has been closed.
- 出错或者尝试向管道写入数据,而这个管道的读端已关闭。
- POLLHUP:
- Hang up (only returned in revents; ignored in events). Note that when reading from a channel such as a pipe or a stream socket, this event merely indicates that the peer closed its end of the channel. Subsequent reads from the channel will return 0 (end of file) only after all outstanding data in the channel has been consumed.
- 挂起
图来自:(221条消息) poll函数详解_青季的博客-CSDN博客_poll函数
函数使用
相比于select,poll没有最大文件描述符的限制。但poll也有一些弊端,就是需要遍历传入的fds数组的
revents去一个个检查是否有事件发生.
以下这个例子是监听
listenfd的连接请求以及其收到的数据
- server
#include <poll.h>
#include <fcntl.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <errno.h>
int main(int argc, char *argv[]) {
nfds_t nfds;
struct sockaddr_in addr;
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(30000);
inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
int ret = bind(listenfd, (struct sockaddr*)&addr, sizeof(addr));
ret = listen(listenfd, 5);
struct pollfd pollfds[100];
int user_cnt = 0;
pollfds[0].fd = listenfd;
pollfds[0].events = POLLIN;
while(1) {
int ready = poll(pollfds, user_cnt + 1, -1);
for(int i = 0; i < user_cnt + 1; ++i) {
if(pollfds[i].fd == listenfd) {
struct sockaddr_in cli_addr;
socklen_t len = sizeof(addr);
int cli_fd = accept(listenfd, (struct sockaddr*)&cli_addr, &len);
printf("new client %d connect\n", cli_fd);
user_cnt++;
pollfds[user_cnt].fd = cli_fd;
pollfds[user_cnt].events = POLLIN;
}
else {
char buf[1024];
int ret = read(pollfds[i].fd, buf, sizeof(buf) - 1);
printf("data from cilent %d : %s\n", pollfds[i].fd, buf);
}
}
}
return 0;
}
- client
#include <poll.h>
#include <fcntl.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <errno.h>
int main(int argc, char const *argv[]) {
struct sockaddr_in addr;
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(30000);
inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
int ret = connect(sockfd, (struct sockaddr*)&addr, sizeof(addr));
while(1) {
char buf[1024];
scanf("%s", buf);
ret = write(sockfd, buf, strlen(buf));
}
return 0;
}

浙公网安备 33010602011771号