您的位置:首页 > 运维架构 > Linux

Linux——高性能服务器编程——select&poll&epoll

2017-11-21 15:10 661 查看
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;  微秒

}

返回值: 0:超时
                -1 :出错
               >0:就绪文件描述符的个数

poll:
int poll(struct pollfd *fds, int nfds, int timeout);
fds:数组,数组中记录所有监听的文件描述符和关注的事件类型
struct pollfd
{
     int fd;       /*文件描述符*/
     short events;     /*注册的事件*/
     short revent;       /*实际发生的事件,由内核填充 */
};
nfds:数组元素个数
timeout:超时时间,单位毫秒,-1表示永远阻塞,0表示立即返回
返回值: 0:超时

                -1 :出错

               >0:就绪文件描述符的个数

epoll:
#include<sys/epoll.h>
int epoll_create(int size);//创建一个内核事件表,返回内核事件表的标志符id
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epfd:内核事件表的id,epoll_create的返回值
op:操作,EPOLL_CTL_ADD,EPOLL_CTL_MOD ,EPOLL_CTL_DEL
event:事件类型

struct epoll_event
{
      int events;
      epoll_data_t  data;
};
struct epoll_data_t
{
      void *ptr;
       int fd;
       int u32;
       int u64;

};
int epoll_wait(int epfd, struct epoll_event *revent, int maxevents, int timeout);
revents是一个用于记录内核就绪事件的数组。

三组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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

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);
}
}
}

}
}


cli.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>

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);
}
}
结果:



poll.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <poll.h>

#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);
}
}
}
}
}


结果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐