在上一篇博客中我们简单的select进行了实现,但是我们都知道select是早期的一个服务器,有缺点,缺点也很明显,它支持的文件描述符只有1024个是比较少的,所以当请求达到一定数量的时候,他就不能被称为高性能服务器了,他就会越来越慢知道挂掉;所以,我们来简单来介绍poll来对select进行改进;
poll的优缺点
优点
- poll不需要开发者来计算最大文件描述符+1
- poll在应付大数目的文件描述符的时候比select要快
- 它没有最大的连接数限制,它是基于链表来存储的
缺点 ####
- 大量的fd的数组被整体复制于用户态和内核态之间,而不管这样的复制是否有意义
- 与select一样,poll返回后需要轮询pollfd来获取就绪的文件描述符
poll介绍 ###
poll原型
#include<poll.h>
int poll(struct pollfd* fds, nfds_t nfds, int timeout);
第一个参数
struct pollfd
{
int fd; //文件描述符
short events; //注册的事件
short revents;//实际发生的事件,由内核填充
}
第二个参数
nfds:指定被监听事件集合;
第三个参数
timeout参数指定poll的超时值
-1:poll阻塞式等待
0 :poll调用立即返回
poll简单实现 ###
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <poll.h>
#define NUMBER_MAX_USER 1024
static void usage(const char* proc)
{
printf("Usage: %s [local_ip] [local_port]\n", proc);
}
int startup(const char* _ip, int _port)
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock < 0)
{
perror("socket");
return 2;
}
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 = inet_addr(_ip);
if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0)
{
perror("bind");
return 3;
}
if(listen(sock, 10) < 0)
{
perror("listen");
return 4;
}
return sock;
}
int main(int argc, char *argv[])
{
if(argc != 3)
{
usage(argv[0]);
return 1;
}
int listen_sock = startup(argv[1], atoi(argv[2]));
struct pollfd pofd[NUMBER_MAX_USER];
int i = 1;
for(; i < NUMBER_MAX_USER; i++)
{
pofd[i].fd = -1;
pofd[i].events = 0;
}
pofd[0].fd = listen_sock;
pofd[0].events = POLLIN;
pofd[0].revents = 0;
while(1)
{
// int timeout = 1000;
int num = poll(pofd, NUMBER_MAX_USER, /*timeout*/-1);
switch(num)
{
case -1:
{
perror("poll");
}
break;
case 0:
{
printf("timeout...\n");
}
break;
default:
{
for(i = 0; i < NUMBER_MAX_USER; i++)
{
if(pofd[i].fd == -1)
continue;
else if((pofd[i].fd == listen_sock) && (pofd[i].revents == POLLIN))
{
struct sockaddr_in client;
socklen_t len = sizeof(client);
int new_sock = accept(pofd[i].fd, (struct sockaddr*)&client, &len);
if(new_sock < 0)
{
perror("accept");
return 5;
}
printf("get new client... [%s:%d]\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
int j = 0;
for(; j < NUMBER_MAX_USER; j++)
{
if(pofd[j].fd == -1)
break;
}
if(j == NUMBER_MAX_USER)
{
printf("server is full\n");
close(new_sock);
break;
}
else
{
pofd[j].fd = new_sock;
pofd[j].events = POLLIN/* | POLLOUT*/;
}
}//listen_sock only read
else if(pofd[i].revents & POLLIN)
{
char *buf[1024];
int ret = read(pofd[i].fd, buf, sizeof(buf)-1);
if(ret < 0)
{
perror("read");
return 6;
}
else if(ret == 0)
{
printf("time out...");
close(pofd[i].fd);
pofd[i].fd = -1;
continue;
}
else
{
buf[ret] = 0;
printf("client ###:%s", buf);
}
}//other read
else if(pofd[i].revents & POLLOUT)
{
const char* msg = "hello world\n";
write(pofd[i].fd, msg, strlen(msg));
close(pofd[i].fd);
pofd[i].fd = -1;
}//write
}//for
}//default
break;
}//switch
}
return 0;
}