I/O复用:
多进程、 多线程、 进程池、 线程池每一个执行序列在同一时刻只能处理一个 socket(监
听、 链接)。 以线程池为例: 如果创建 N个线程, 同一时刻只能处理N的客户连接。I/O复
用: 在一个进程或者一个线程中, 同时监听多个 socket。 当有socket上有事件发生时, 程序
才会接受数据。
select:
int n = select(int nfds, fd_set *read, fd_set *write,fd_set *except,struct timeval *timeout);
nfds:监听的最大文件描述符值 + 1;
read write except:select监听的文件描述符上的可读,可写,异常事件。
struct fd_set
{
long int fd_sets[32];
}
struct timeval
{
long tv_sec; 秒数
long tv_usec; 微秒
}
三组I/O复用的函数比较:
(1)事件集:
Select的参数类型fd_set没有将文描和事件绑定,所以需要三个这种类型的参数来分别传入和输出可读,可写和异常事件;poll则修改pollfd结构体中的revents事件,event事件保持不变,这两者时间复杂度都为O(n);但是epoll在内核中维护一个事件表,提供epoll_ctl来添加,删除,修改事件。Epoll_wait的events返回就绪的事件,则只返回就绪的文描个数
Poll, epollselest最大文件描述符个数受到限制,时间复杂度O(1)
(2)最大支持文件描述符数:
Poll和epoll分别用nfds和maxevents来指定最多监听多少个文描个数和事件,两个都能达到系统最大允许的65535,select允许的最大文描个数有限制,一般不可修改。
(3)工作模式
Select和poll工作在低效的LT模式,epoll工作在高效的ET模式,epoll支持EPOLLONESHOT事件。
(4)具体实现
Select和poll采用轮询的方式,每次调用都要扫描整个注册文件描述符的集合;epoll_wait采用回调的方式。
select.c#include
#include
#include
#include
#include
#include
#include
#include
#include
void main()
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
assert(sockfd != -1);
struct sockaddr_in ser, cli;
memset(&ser, 0, sizeof(ser));
ser.sin_family = AF_INET;
ser.sin_port = htons(6000);
ser.sin_addr.s_addr = inet_addr("192.168.1.120");
int res = bind(sockfd, (struct sockaddr*)&ser, sizeof(ser));
assert(res != -1);
listen(sockfd, 5);
int nfds = sockfd + 1;
fd_set read;//
FD_ZERO(&read);
int fds[128];//记录所有要监听的文描
//memset(fds, -1, 128);
int i = 0;
for(;i < 128;++i)
{
fds[i] = -1;
}
fds[0] = sockfd;
int max = 0;
while(1)
{
FD_ZERO(&read);//清空
int j = 0;
for(; j < 128; j++)
{
if(fds[j] != -1)
{
if(max < fds[j])
max = fds[j];
FD_SET(fds[j], &read);
}
}
int n = select(max+1, &read, NULL, NULL, NULL);
//将fds数组中的文描设置到read,write,except中
if(n < 0)
{
printf("error\n");
exit(0);
}
if(n == 0) // 超时
{
printf("time out\n");
continue;
}
int i = 0;
//循环判断fd中每个文描是否是就绪事件
for(; i < 128; ++i)
{
if(fds[i] == -1)
continue;
if(FD_ISSET(fds[i], &read))
{
if(fds[i] == sockfd)
{
int len = sizeof(cli);
int c = accept(sockfd, (struct sockaddr*)&cli, &len);
int i = 0;
for(; i < 128; ++i)
{
if(fds[i] == -1)
{
fds[i] = c;
break;
}
}
}
else
{
char buff[128] = {0};
int n = recv(fds[i], buff, 128, 0);
if(n <= 0)
{
close(fds[i]);
fds[i] = -1;
continue;
}
printf("%s\n", buff);
send(fds[i], "ok", 2, 0);
}
}
}
}
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
void main()
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
assert(sockfd != -1);
struct sockaddr_in ser, cli;
memset(&ser, 0, sizeof(ser));
ser.sin_family = AF_INET;
ser.sin_port = htons(6000);//端口号
ser.sin_addr.s_addr = inet_addr("192.168.1.120");
int res = connect(sockfd, (struct sockaddr*)&ser, sizeof(ser));
assert(res != -1);
while(1)
{
printf("please input: ");
fflush(stdout);
char buff[128] = {0};
fgets(buff, 128, stdin);
if(strncmp(buff, "end", 3) == 0)
{
close(sockfd);
break;
}
send(sockfd, buff, strlen(buff) - 1, 0);
memset(buff, 0, 128);
recv(sockfd, buff, 127, 0);
printf("%s\n", buff);
}
}
结果:#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAX 128
void Init(struct pollfd *fds, int len)
{
int i = 0;
for(; i < len; ++i)
{
fds[i].fd = -1;
fds[i].events = 0;
}
}
void AddFd(struct pollfd *fds, int len, int fd)
{
int i = 0;
for(; i < len; ++i)
{
if(fds[i].fd == -1)
{
fds[i].fd = fd;
fds[i].events = POLLIN;
break;
}
}
}
void main()
{
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
assert(listenfd != -1);
struct sockaddr_in ser, cli;
memset(&ser, 0, sizeof(ser));
ser.sin_family = AF_INET;
ser.sin_port = htons(6000);
ser.sin_addr.s_addr = inet_addr("192.168.1.120");
int res = bind(listenfd, (struct sockaddr*)&ser, sizeof(ser));
assert(res != -1);
listen(listenfd, 5);
struct pollfd fds[MAX];
Init(fds, MAX);
AddFd(fds, MAX, listenfd);
while(1)
{
int n = poll(fds, MAX, -1);
assert(n != -1);
if(n == 0)
{
printf("time out\n");
continue;
}
int i = 0;
for(; i < MAX; ++i)
{
if(fds[i].fd == -1)
{
continue;
}
if(fds[i].revents & POLLIN)
{
int fd = fds[i].fd;
if(fd == listenfd)
{
int len = sizeof(cli);
int c = accept(fd, (struct sockaddr *)&cli, &len);
assert(c != -1);
printf("one client link\n");
AddFd(fds, MAX, c);
}
else
{
char buff[128] = {0};
int n = recv(fd, buff, 127, 0);
if(n <= 0)
{
printf("client unlink\n");
close(fd);
fds[i].fd = -1;
fds[i].events = 0;
continue;
}
printf("%d : %s\n", fd, buff);
send(fd, "OK", 2, 0);
}
}
}
}
}