IO复用-poll系统调用
1.poll API
poll和select类似,都是在超时时间内轮训集合内的就绪文件描述符。原型如下;
#include <poll.h>
int poll(struct pollfd *fds, n_fds_t nfds, int timeout);
//pollfd定义如下
struct pollfd
{
int fd; //文件描述符
short events; //注册的事件,一次可设置多个,是一系列事件的按位或
short revents; //实际发生的事件,由内核填充
};
参数说明:
- fds结构体可指定我们感兴趣的文件描述符上发生的可读、写、异常事件;
- nfds指定集合fds的大小;
- timeout单位为毫秒,设置为-1时,永远阻塞直到有事件就绪;设置为0立即返回;
- 返回值和select一致;
2.poll支持的事件类型
poll支持的事件类型如下表:
事件 | 描述 | 是否可作为输入 | 是否可作为输出 |
---|---|---|---|
POLLIN | 数据(包含普通和优先数据)可读 | 是 | 是 |
POLLRDNORM | 普通数据可读 | 是 | 是 |
POLLBAND | 优先级带数据可读(Linux不支持) | 是 | 是 |
POLLPRI | 高优先级数据可读(如TCP带外数据) | 是 | 是 |
POLLOUT | 数据(包含普通和优先数据)可写 | 是 | 是 |
POLLWRNORM | 普通数据可写 | 是 | 是 |
POLLWRBAND | 优先级带数据可写 | 是 | 是 |
POLLRDHUP | TCP连接被对方关闭或对方关闭写操作(由GNU引入) | 是 | 是 |
POLLERR | 错误 | 否 | 是 |
POLLHUP | 挂起(如管道写被关闭后,读端将收到POLLHUP) | 否 | 是 |
POLLNVAL | 文件描述符没打开 | 否 | 是 |
3.poll实例-处理带外数据
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <poll.h>
#define CLIENT_SIZE 5
int main(int argc, char const *argv[])
{
if (argc != 3)
{
printf("usage: %s <IP> <Port>\n", argv[0]);
return 1;
}
const char *ip = argv[1];
int port = atoi(argv[2]);
/*设置sockaddr*/
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
inet_pton(AF_INET, ip, &addr.sin_addr);
addr.sin_port = htons(port);
/*创建socket*/
int sock = socket(PF_INET, SOCK_STREAM, 0);
assert(sock >= 0);
/*命名socket*/
int result = bind(sock, (struct sockaddr*)&addr, sizeof(addr));
assert(result != -1);
/*进入监听状态*/
result = listen(sock, 5);
assert(result != -1);
struct sockaddr_in client_addr;
socklen_t client_addrlen = sizeof(client_addr);
/*等待接收客户端连接*/
int connfd = accept(sock, (struct sockaddr*)&client_addr, &client_addrlen);
if (connfd < 0)
{
printf("accept failed, errno: %d\n", errno);
close(sock);
}
char buf[1024];
struct pollfd pfd[CLIENT_SIZE];
for (; ; )
{
memset(buf, '\0', sizeof(buf));
memset(pfd, 0, sizeof(pfd));
/*监听connfd的可读和高优先级可读事件*/
pfd[0].fd = connfd;
pfd[0].events = POLLIN | POLLPRI;
/*poll阻塞等待事件就绪*/
result = poll(pfd, CLIENT_SIZE, -1);
if (result < 1)
{
printf("poll failed!\n");
break;
}
for (int i = 0; i < CLIENT_SIZE; i++)
{
if (pfd[i].fd == connfd )
{
if (pfd[i].revents & POLLPRI) //高优先级读数据(带外)
{
result = recv(connfd, buf, sizeof(buf)-1, MSG_OOB);
if (result <= 0)
{
break;
}
printf("get %d bytes of OOB data: %s", result, buf);
}
else if (pfd[i].revents & POLLIN) //普通读数据
{
result = recv(connfd, buf, sizeof(buf)-1, 0);
if (result <= 0)
{
break;
}
printf("get %d bytes of normal data: %s", result, buf);
}
}
}
}
close(connfd);
close(sock);
return 0;
}