poll其实和select服务器实现的功能是一样的,但是poll中有具体的函数来帮助我们实现I/O多路转接。
我们来认识一下这些函数。
poll函数
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout)
pollfd 结构
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. */
};
pollfd结构中将输入事件和输出事件分开了,用结构体来管理,解决了select每次都要重置fd_set,将关心的事件和就绪的事件进行区分。
参数:
* fds是一个poll函数监听的结构列表,每一个元素中,包含了三部分内容:文件描述符,监听的事件集合,返回的事件集合。
* nfds表示fds数组的长度。
* timeout表示poll函数的超时时间,单位是毫秒。
events、revents的取值
事件 | 描述 | 是否可作输入 | 是否可作输出 |
---|---|---|---|
POLLIN | 数据(包括普通数据和优先数据)可读 | 是 | 是 |
POLLRDNORM | 普通数据可读 | 是 | 是 |
POLLRDBAND | 优先带数据可读(Linux不支持) | 是 | 是 |
POLLPRI | 高优先级数据可读 | 是 | 是 |
POLLOUT | 数据可写 | 是 | 是 |
POLLWRNORM | 普通数据可写 | 是 | 是 |
POLLWRBAND | 优先数据可写 | 是 | 是 |
POLLRDHUP | TCP连接被对方关闭,或对方关闭了写操作,由GNU引入 | 是 | 是 |
POLLERR | 错误 | 否 | 是 |
POLLHUP | 挂起 | 否 | 是 |
POLLNVAL | 文件描述符没有打开 | 否 | 是 |
poll的返回值和select的一样。
* 小于0,表示出错。
* 等于0,表示poll函数等待超时。
* 大于0,表示poll由于监听的文件描述符就绪而返回。
socket就绪条件
1.读就绪
* socket内核中,接收缓冲区中的字节数大于等于低水位SO_RCVLOWAT,此时可以无阻塞的读该文件描述符,并且返回值大于0.
* socket TCP通信中,对端关闭连接,此时对该socket读,则返回0.
* 监听的socket上有新的连接请求
* socket上有未处理的错误。
2.写就绪
* socket内核中,发送缓冲区中的可用字节数,大于等于低水位SO_SNDLOWAT,此时可以无阻塞的写,并且返回值大于0.
* socket的写操作被关闭,对于一个写操作被关闭的socket进行写操作,会出发SIGPIPE信号.
* socket使用非阻塞connect连接成功或失败之后。
* socket上有未读取的错误。
3.异常就绪
* socket上收到带外数据。
poll的优点
* pollfd结构中包含了要监视额events和revents,不再使用select值传递的方式,接口较简单。
* poll中文件描述符数量没有限制,是因为可以在堆上malloc空间。
事实上,文件描述符是有大小限制的,不可能是无限的。因为PCB指向文件描述符表的限制,所以poll的性能可能最后趋于平静。
poll的缺点
当poll中的文件描述符越来越多的时候,poll返回后,需要轮询pollfd来获取就绪的描述符。
和select一样,每次调用都需要把大量的pollfd结构从用户态拷贝到内核中。
随着文件描述符的增多,可能会出现只有少量的事件就绪,会导致效率降低。poll的性能是从低到高,最后趋于平静的。
使用poll监视标准输入
#include <stdio.h>
#include <poll.h>
#include <unistd.h>
int main()
{
struct pollfd fd;
fd.fd = 0;
fd.events = POLLIN;//数据可读
for(;;){
int ret = poll(&fd, 1, 2000);
if(ret<0){
perror("poll");
continue;
}
if(ret==0){
printf("poll timeout\n");
continue;
}
if(fd.revents == POLLIN){
char buf[1024] = {0};
read(0, buf, sizeof(buf-1));
printf("输入:%s",buf);
}
}
return 0;
}
poll多路转接服务器
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <poll.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
typedef struct pollfd pollfd;
int start_up(int port)
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock<0)
{
perror("socket");
exit(2);
}
//设置没有TIME_WAIT
int opt=1;
setsockopt(sock, SOL_SOCKET,SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(port);
local.sin_addr.s_addr = htonl(INADDR_ANY);
int ret = bind(sock, (struct sockaddr*)&local, sizeof(local));
if(ret < 0)
{
perror("bind");
exit(3);
}
ret = listen(sock, 5);
{
if(ret < 0)
{
perror("listen");
exit(4);
}
}
return sock;
}
void Init(pollfd* fd_list, int size)
{
int i =0;
for(i=0; i<size; ++i)
{
fd_list[i].fd = -1;
fd_list[i].events = 0;
fd_list[i].revents = 0;
}
}
void Add(int fd, pollfd* fd_list, int size)
{
int i=0;
for(; i<size; ++i)
{
if(fd_list[i].fd == -1){
fd_list[i].fd = fd;
fd_list[i].events = POLLIN;//数据可读
break;
}
}
}
int main(int argc, char* argv[])
{
if(argc != 2)
{
printf("Usage: ./server [port]\n");
return 1;
}
int listen_fd = start_up(atoi(argv[1]));
pollfd fd_list[1024];
Init(fd_list, sizeof(fd_list)/sizeof(pollfd));
Add(listen_fd, fd_list, sizeof(fd_list)/sizeof(pollfd));
for(;;)
{
int ret = poll(fd_list, sizeof(fd_list)/sizeof(pollfd),9000);
if(ret < 0)
{
perror("poll");
continue;
}
if(ret == 0)
{
printf("timeout...");
continue;
}
int i=0;
for(;i<sizeof(fd_list)/sizeof(pollfd);++i){
if(fd_list[i].fd==-1)
continue;
if(!fd_list[i].revents & POLLIN)
continue;
if(fd_list[i].fd==listen_fd)
{
struct sockaddr_in client;
socklen_t len = sizeof(client);
int connect_fd = accept(listen_fd, (struct sockaddr*)&client,&len);
if(connect_fd < 0){
perror("accept");
continue;
}
Add(connect_fd, fd_list, sizeof(fd_list)/sizeof(pollfd));
}
else
{
char buf[1024]={0};
ssize_t read_size = read(fd_list[i].fd, buf, sizeof(buf)-1);
if(read_size < 0)
{
perror("read");
continue;
}
if(read_size == 0)
{
printf("client is closed\n");
close(fd_list[i].fd);
fd_list[i].fd = -1;
}
printf("client:>%s\n",buf);
write(fd_list[i].fd, buf, strlen(buf));
}
}
}
}