首先看一下man文档中这三个函数的定义:

select函数:

#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
void FD_CLR(int fd, fd_set *set); // 从集合中删除指定文件描述符
int FD_ISSET(int fd, fd_set *set); // 检查集合中指定文件描述符是否准备好
void FD_SET(int fd, fd_set *set); // 将一个文件描述符加入到集合中
void FD_ZERO(fd_set *set); // 将一个集合请空

参数说明:

nfds:所要监视的文件描述符范围,即被监听的文件描述符最大值加一。

readfds:被监视的可读操作的文件描述符。

writefds:被监视的可写操作的文件描述符。

exceptfds:被监视的文件错误异常的文件描述符。

下面是使用select的一个echo服务端例子:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/select.h>
#include <iostream>
#define BUF_SIZE 100

void error_handling(char* message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}

int main(int argc, char* argv[])
{
int serv_sock, clnt_sock;
struct sockaddr_in serv_adr, clnt_adr;
struct timeval timeout;
fd_set reads, cpy_reads;

socklen_t adr_sz;
int fd_max, str_len, fd_num;
char buf[BUF_SIZE];

if ((serv_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket error");
return 1;
}

memset(&serv_adr, 0, sizeof(serv_adr));
serv_adr.sin_family = AF_INET;
serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_adr.sin_port = htons(8000);

if (bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr)) == -1) {
error_handling("bind error");
}

if (listen(serv_sock, 5) == -1) {
error_handling("listen error");
}

FD_ZERO(&reads); // 初始化
// 注册serv_sock
FD_SET(serv_sock, &reads);
fd_max = serv_sock;

while (true) {
cpy_reads = reads;
timeout.tv_sec = 5;
timeout.tv_usec = 5000;

// 监听服务端socket和与客户端连接的服务端socket的read事件
if ((fd_num = select(fd_max + 1, &cpy_reads, 0, 0, &timeout)) == -1)
break;

if (fd_num == 0)
continue;

if (FD_ISSET(serv_sock, &cpy_reads)) { // 受理客户端连接请求
adr_sz = sizeof(clnt_adr);
clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz);
FD_SET(clnt_sock, &reads);

if (fd_max < clnt_sock)
fd_max = clnt_sock;

std::cout << "connected client: " << clnt_sock << std::endl;
}
else { // 转发客户数据
str_len = read(clnt_sock, buf, BUF_SIZE);

if (str_len == 0) { // 客户端发送退出EOF
// 取消注册该推出的套接字
FD_CLR(clnt_sock, &reads);
close(clnt_sock);
std::cout << "closed client: " << clnt_sock << std::endl;
}
else {
// 回写到客户端
write(clnt_sock, buf, str_len);
}
}
}

close(serv_sock);
return 0;
}

 

poll函数:

#include <poll.h>

int poll(struct pollfd *fds, nfds_t nfds, int timeout);
struct pollfd {
  int fd; /* file descriptor */
  short events; /* requested events */
  short revents; /* returned events */
};


参数说明:

fds:所监听的描述符。

nfds:所监听的文件描述符数目。

timeout:超时时间,单位是毫秒,设为-1表示永不超时。

下面是poll函数的事件标志符:

select、poll、epoll_客户端

 

 

 epoll:

int epoll_create1(int; // 创建epoll实例
int epoll_ctl(int epfd, int op, int; // 管理epoll事件

函数参数:

  • epfd : epoll实例的fd
  • op : 操作标志,下文会描述
  • fd : 监控对象的fd
  • event : 事件的内容,下文描述

op可以有3个值,分别为:

  • EPOLL_CTL_ADD : 添加监听的事件
  • EPOLL_CTL_DEL : 删除监听的事件
  • EPOLL_CTL_MOD : 修改监听的事件

typedefunion epoll_data { void *ptr; int fd; __uint32_t u32; __uint64_t u64; } epoll_data_t;

struct epoll_event__uint32_t events; /* Epoll events */epoll_data_t data; /* User data variable */ };

 

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int; // 等待epoll事件

函数参数:

  • epfd : epoll实例的fd
  • events : 储存事件的数组首地址
  • maxevents : 最大事件的数量
  • timeout : 等待的最长时间

使用epoll的echo服务端例子:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <iostream>

#define BUF_SIZE 100
#define EPOLL_SIZE 50

void error_handling(char* message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}

int main(int argc, char* argv[])
{
int serv_sock, clnt_sock;
struct sockaddr_in serv_adr, clnt_adr;
socklen_t adr_sz;
int str_len, i;
char buf[BUF_SIZE];

// 类似select的fd_set变量查看监视对象的状态变化
// epoll_event结构体将发生变化的文件描述符集中到一起
struct epoll_event* ep_events;
struct epoll_event event;
int epfd, event_cnt;

if ((serv_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
error_handling("socket error");
}

memset(&serv_adr, 0, sizeof(serv_adr));
serv_adr.sin_family = AF_INET;
serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_adr.sin_port = htons(8000);

if (bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr)) < 0) {
error_handling("bind error");
}

if (listen(serv_sock, 5) < 0) {
error_handling("listen error");
}

// 创建文件描述符的保存空间成为epoll例程
epfd = epoll_create(EPOLL_SIZE);
ep_events = (struct epoll_event*)malloc(sizeof(struct epoll_event) * EPOLL_SIZE);

// 添加读取事件的监视(注册)
event.events = EPOLLIN; // 读取数据事件
event.data.fd = serv_sock;
epoll_ctl(epfd, EPOLL_CTL_ADD, serv_sock, &event);

while (true) {
// 响应事件 返回发生事件的文件描述符数
event_cnt = epoll_wait(epfd, ep_events, EPOLL_SIZE, -1); // 传入-1 表示一直等待直到事件发生

if (event_cnt == -1) {
std::cout << "epoll_wait error" << std::endl;
break;
}

// 服务端套接字个客户端套接字
for (i = 0; i < event_cnt; i++) {
if (ep_events[i].data.fd == serv_sock) { // 服务端与客户端建立连接
adr_sz = sizeof(clnt_adr);
clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz);
event.events = EPOLLIN;
event.data.fd = clnt_sock;
epoll_ctl(epfd, EPOLL_CTL_ADD, clnt_sock, &event);
std::cout << "connected client: " << clnt_sock << std::endl;
}
else { // 连接后的客户端 传递数据
str_len = read(ep_events[i].data.fd, buf, BUF_SIZE);

if (str_len == 0) {
// 删除事件
epoll_ctl(epfd, EPOLL_CTL_DEL, ep_events[i].data.fd, NULL);
close(ep_events[i].data.fd);
std::cout << "close client: " << ep_events[i].data.fd << std::endl;
}
else {
write(ep_events[i].data.fd, buf, str_len);
}
}
}
}

close(serv_sock);
close(epfd);
return 0;
}

select、poll、epoll比较:

select、poll、epoll_客户端_02

 

  

转载请注明出处