select与poll区别
参考知乎select与poll区别
区别1:select使用的是定长数组,而poll是通过用户自定义数组长度的形式(pollfd[])。
区别2:select只支持最大fd < 1024,如果单个进程的文件句柄数超过1024,select就不能用了(可以修改有上限)。poll在接口上无限制,考虑到每次都要拷贝到内核,一般文件句柄多的情况下建议用epoll。
区别3:select由于使用的是位运算,所以select需要分别设置read/write/error fds的掩码。而poll是通过设置数据结构中fd和event参数来实现read/write,比如读为POLLIN,写为POLLOUT,出错为POLLERR:
区别4:select中fd_set是被内核和用户共同修改的,所以要么每次FD_CLR再FD_SET,要么备份一份memcpy进去。而poll中用户修改的是events,系统修改的是revents。所以参考muduo的代码,都不需要自己去清除revents,从而使得代码更加简洁。
区别5:select的timeout使用的是struct timeval *timeout,poll的timeout单位是int。
区别6:select使用的是绝对时间,poll使用的是相对时间。
区别7:select的精度是微秒(timeval的分度),poll的精度是毫秒。
区别8:select的timeout为NULL时表示无限等待,否则是指定的超时目标时间;poll的timeout为-1表示无限等待。所以有用select来实现usleep的。
区别9:理论上poll可以监听更多的事件,比如sysroot/usr/include/asm-generic/poll.h中有
至于epoll,其实可以理解为poll的add/del/modify的增强版,使得用户不需要去关心最大的集合,而只需要去关心在需要add/delete的时候往内核注册一下
#define POLLIN 0x0001
#define POLLPRI 0x0002
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define POLLOUT 0x0004
#define POLLERR 0x0008
#define POLLHUP 0x0010
#define POLLNVAL 0x0020
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define POLLRDNORM 0x0040
#define POLLRDBAND 0x0080
#ifndef POLLWRNORM
#define POLLWRNORM 0x0100
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#endif
#ifndef POLLWRBAND
#define POLLWRBAND 0x0200
#endif
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#ifndef POLLMSG
#define POLLMSG 0x0400
#endif
#ifndef POLLREMOVE
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define POLLREMOVE 0x1000
#endif
#ifndef POLLRDHUP
#define POLLRDHUP 0x2000
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#endif
#define POLLFREE 0x4000
#define POLL_BUSY_LOOP 0x8000
struct pollfd {
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
int fd;
short events;
short revents;
};
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/poll.h>
#define BUFFER_LENGTH 1024
#define POLL_SIZE 1024
#define EPOLL_SIZE 1024
int client_route(void *arg) {
int clientfd = *(int*)arg;
while (1) {
char buffer[BUFFER_LENGTH] = {0};
int ret = recv(clientfd, buffer, BUFFER_LENGTH, 0);
if (ret < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
printf("read all data\n");
}
//close(clientfd);
return -1;
} else if (ret == 0) {
printf("disconnect \n");
//close(clientfd);
return 0;
} else {
printf("Recv:%s, %d Bytes\n", buffer, ret);
return ret;
}
}
}
//方式1 的回调函数
void* client_callback(void *arg) {
int clientfd = *(int*)arg;
while (1) {
char buffer[BUFFER_LENGTH] = {0};
int ret = recv(clientfd, buffer, BUFFER_LENGTH, 0);
if (ret < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
printf("read all data\n");
}
//close(clientfd);
return NULL;
} else if (ret == 0) {
printf("disconnect \n");
//close(clientfd);
return NULL;
} else {
printf("Recv:%s, %d Bytes\n", buffer, ret);
//return NULL;
}
}
}
//使用方式 绑定某个端口运行
//gcc server_epoll.c -o server_epoll
//./server_epoll 12000
int main(int argc, char *argv[]) {
if (argc < 2) {
printf("Paramter Error\n");
return -1;
}
int port = atoi(argv[1]);
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("socket");
return -1;
}
struct sockaddr_in addr;
memset(&addr, 0, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)) < 0) {
perror("bind");
return 2;
}
if (listen(sockfd, 5) < 0) {
perror("listen");
return 3;
}
#if 0
//方式1:创建线程处理 accpet新来的客户端 一个线程对应一个客户端
while (1) { //c10k
//
struct sockaddr_in client_addr;
memset(&client_addr, 0, sizeof(struct sockaddr_in));
socklen_t client_len = sizeof(client_addr);
int clientfd = accept(sockfd, (struct sockaddr*)&client_addr, &client_len);
if (clientfd <= 0) continue;
pthread_t thread_id;
int ret = pthread_create(&thread_id, NULL, client_callback, &clientfd);
if (ret < 0) {
perror("pthread_create");
exit(1);
}
}
#elif 1
//方式2 采用select 多路复用 适合数量1024以下 具体见select专题**
fd_set rfds, rset;
FD_ZERO(&rfds);
FD_SET(sockfd, &rfds);
int max_fd = sockfd;
int i = 0;
while (1) {
rset = rfds;
int nready = select(max_fd+1, &rset, NULL, NULL, NULL);
if (nready < 0) {
printf("select error : %d\n", errno);
continue;
}
if (FD_ISSET(sockfd, &rset)) { //accept
struct sockaddr_in client_addr;
memset(&client_addr, 0, sizeof(struct sockaddr_in));
socklen_t client_len = sizeof(client_addr);
int clientfd = accept(sockfd, (struct sockaddr*)&client_addr, &client_len);
if (clientfd <= 0) continue;
char str[INET_ADDRSTRLEN] = {0};
printf("recvived from %s at port %d, sockfd:%d, clientfd:%d\n", inet_ntop(AF_INET, &client_addr.sin_addr, str, sizeof(str)),
ntohs(client_addr.sin_port), sockfd, clientfd);
if (max_fd == FD_SETSIZE) {
printf("clientfd --> out range\n");
break;
}
FD_SET(clientfd, &rfds);
if (clientfd > max_fd) max_fd = clientfd;
printf("sockfd:%d, max_fd:%d, clientfd:%d\n", sockfd, max_fd, clientfd);
if (--nready == 0) continue;
}
for (i = sockfd + 1;i <= max_fd;i ++) {
if (FD_ISSET(i, &rset)) {
char buffer[BUFFER_LENGTH] = {0};
int ret = recv(i, buffer, BUFFER_LENGTH, 0);
if (ret < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
printf("read all data");
}
FD_CLR(i, &rfds);
close(i);
} else if (ret == 0) {
printf(" disconnect %d\n", i);
FD_CLR(i, &rfds);
close(i);
break;
} else {
printf("Recv: %s, %d Bytes\n", buffer, ret);
}
if (--nready == 0) break;
}
}
}
#elif 1
//poll实现效果和select差不多
struct pollfd fds[POLL_SIZE] = {0};
fds[0].fd = sockfd;
fds[0].events = POLLIN;
int max_fd = 0, i = 0;
for (i = 1;i < POLL_SIZE;i ++) {
fds[i].fd = -1;
}
while (1) {
//
int nready = poll(fds, max_fd+1, 5);
if (nready <= 0) continue;
if ((fds[0].revents & POLLIN) == POLLIN) {
struct sockaddr_in client_addr;
memset(&client_addr, 0, sizeof(struct sockaddr_in));
socklen_t client_len = sizeof(client_addr);
int clientfd = accept(sockfd, (struct sockaddr*)&client_addr, &client_len);
if (clientfd <= 0) continue;
char str[INET_ADDRSTRLEN] = {0};
printf("recvived from %s at port %d, sockfd:%d, clientfd:%d\n", inet_ntop(AF_INET, &client_addr.sin_addr, str, sizeof(str)),
ntohs(client_addr.sin_port), sockfd, clientfd);
fds[clientfd].fd = clientfd;
fds[clientfd].events = POLLIN;
if (clientfd > max_fd) max_fd = clientfd;
if (--nready == 0) continue;
}
for (i = sockfd + 1;i <= max_fd;i ++) {
if (fds[i].revents & (POLLIN|POLLERR)) {
char buffer[BUFFER_LENGTH] = {0};
int ret = recv(i, buffer, BUFFER_LENGTH, 0);
if (ret < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
printf("read all data");
}
//close(i);
fds[i].fd = -1;
} else if (ret == 0) {
printf(" disconnect %d\n", i);
close(i);
fds[i].fd = -1;
break;
} else {
printf("Recv: %s, %d Bytes\n", buffer, ret);
}
if (--nready == 0) break;
}
}
}
#else
//epoll实现
int epoll_fd = epoll_create(EPOLL_SIZE);
struct epoll_event ev, events[EPOLL_SIZE] = {0};
ev.events = EPOLLIN;
ev.data.fd = sockfd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sockfd, &ev);
while (1) {
int nready = epoll_wait(epoll_fd, events, EPOLL_SIZE, -1);
if (nready == -1) {
printf("epoll_wait\n");
break;
}
int i = 0;
for (i = 0;i < nready;i ++) {
//有新的客户端连接
if (events[i].data.fd == sockfd) {
struct sockaddr_in client_addr;
memset(&client_addr, 0, sizeof(struct sockaddr_in));
socklen_t client_len = sizeof(client_addr);
int clientfd = accept(sockfd, (struct sockaddr*)&client_addr, &client_len);
if (clientfd <= 0) continue;
char str[INET_ADDRSTRLEN] = {0};
printf("recvived from %s at port %d, sockfd:%d, clientfd:%d\n", inet_ntop(AF_INET, &client_addr.sin_addr, str, sizeof(str)),
ntohs(client_addr.sin_port), sockfd, clientfd);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = clientfd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, clientfd, &ev);
} else {
//有数据可读
int clientfd = events[i].data.fd;
char buffer[BUFFER_LENGTH] = {0};
int ret = recv(clientfd, buffer, BUFFER_LENGTH, 0);
/*
* recv() 返回 -1,且 errno 等于 EAGAIN,表示 recv 操作还没执行完成;
* recv() 返回 -1,且 errno 不等于 EAGAIN,表示 recv 操作遇到系统错误 errno
*/
if (ret < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
printf("read all data");
}
close(clientfd);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = clientfd;
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, clientfd, &ev);
} else if (ret == 0) {
//recv() 返回 0,表示连接已经正常断开;
printf(" disconnect %d\n", clientfd);
close(clientfd);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = clientfd;
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, clientfd, &ev);
break;
} else {
//recv() 返回值大于 0,表示接受数据完毕,返回值即是接受到的字节数;
printf("Recv: %s, %d Bytes\n", buffer, ret);
}
}
}
}
#endif
return 0;
}