随着多线程并发服务器的发展,几种服务器模型应运而生,介绍集中常见的多路IO转接模型.-新闻详情

随着多线程并发服务器的发展,几种服务器模型应运而生,介绍集中常见的多路IO转接模型.


发布时间:2019-04-08责任编辑:满帅 浏览:1373


1 ,select能监听的文件描述符个数受限于FD_SETSIZE,一般为1024,单纯改变进程打开

的文件描述符个数并不能改变select监听文件个数

解决1024以下客户端时使用select是很合适的,但如果链接客户端过多,select采用

的是轮询模型,会大大降低服务器响应效率,不应在select上投入更多精力

2,poll

struct pollfd {

int fd; /* 文件描述符*/

short events; /* 监控的事件*/

short revents; /* 监控事件中满足条件返回的事件*/

};

POLLIN普通或带外优先数据可读,即POLLRDNORM | POLLRDBAND

POLLRDNORM-数据可读

POLLRDBAND-优先级带数据可读

POLLPRI 高优先级可读数据

POLLOUT普通或带外数据可写

POLLWRNORM-数据可写

POLLWRBAND-优先级带数据可写

POLLERR 发生错误

POLLHUP 发生挂起

POLLNVAL 描述字不是一个打开的文件

nfds 监控数组中有多少文件描述符需要被监控

timeout 毫秒级等待

-1:阻塞等,#define INFTIM -1 Linux中没有定义此宏

0:立即返回,不阻塞进程

>0:等待指定毫秒数,如当前系统时间精度不够毫秒,向上取值

3,epoll,这里重点介绍下epoll

epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并

发连接中只有少量活跃的情况下的系统CPU利用率,因为它会复用文件描述符集合来传递结

果而不用迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合,另一点

原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件

异步唤醒而加入Ready队列的描述符集合就行了。

目前epell是linux大规模并发网络程序中的热门首选模型。

epoll除了提供select/ poll那种IO事件的电平触发(Level Triggered)外,还提

供了边沿触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少

epoll_wait/epoll_pwait的调用,提高应用程序效率。

epoll API

1.创建一个epoll句柄,参数size用来告诉内核监听的文件描述符个数,跟内存大小有

关#include

int epoll_create(int size)

size:告诉内核监听的数目

2.控制某个epoll监控的文件描述符上的事件:注册、修改、删除。

#include <sys/epoll.h>

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

epfd:为epoll_creat的句柄

op:表示动作,用3个宏来表示:

EPOLL_CTL_ADD(注册新的fd到epfd),

EPOLL_CTL_MOD(修改已经注册的fd的监听事件),

EPOLL_CTL_DEL(从epfd删除一个fd);

event:告诉内核需要监听的事件

struct epoll_event {

__uint32_t events; /* Epoll events */

epoll_data_t data; /* User data variable */

};

EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭)

EPOLLOUT:表示对应的文件描述符可以写

EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来)

EPOLLERR:表示对应的文件描述符发生错误

EPOLLHUP:表示对应的文件描述符被挂断;

EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来

说的

EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需

要再次把这个socket加入到EPOLL队列里

3.等待所监控文件描述符上有事件的产生,类似于select()调用。

#include <sys/epoll.h>

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)

events:用来从内核得到事件的集合,

maxevents:告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size,

timeout:是超时时间

-1:阻塞

0:立即返回,非阻塞

>0:指定微秒

返回值:成功返回有多少文件描述符就绪,时间到时返回0,出错返回-1

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <sys/epoll.h>

#include <errno.h>

#include "wrap.h"

#define MAXLINE 80

#define SERV_PORT 8000

#define OPEN_MAX 1024

int main(int argc, char *argv[])

{

int i, j, maxi, listenfd, connfd, sockfd;

int nready, efd, res;

ssize_t n;

char buf[MAXLINE], str[INET_ADDRSTRLEN];

socklen_t clilen;

int client[OPEN_MAX];

struct sockaddr_in cliaddr, servaddr;

struct epoll_event tep, ep[OPEN_MAX];

listenfd = Socket(AF_INET, SOCK_STREAM, 0);

bzero(&servaddr, sizeof(servaddr));

servaddr.sin_family = AF_INET;

servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

servaddr.sin_port = htons(SERV_PORT);

Bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr));

Listen(listenfd, 20);

for (i = 0; i < OPEN_MAX; i++)

client[i] = -1;

maxi = -1;

efd = epoll_create(OPEN_MAX);

if (efd == -1)

perr_exit("epoll_create");

tep.events = EPOLLIN; tep.data.fd = listenfd;

res = epoll_ctl(efd, EPOLL_CTL_ADD, listenfd, &tep);

if (res == -1)

perr_exit("epoll_ctl");

for ( ; ; ) {

nready = epoll_wait(efd, ep, OPEN_MAX, -1); /* 阻塞监听*/

if (nready == -1)

perr_exit("epoll_wait");

for (i = 0; i < nready; i++) {

if (!(ep[i].events & EPOLLIN))

continue;

if (ep[i].data.fd == listenfd) {

clilen = sizeof(cliaddr);

connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);

printf("received from %s at PORT %d

", inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), ntohs(cliaddr.sin_port));

for (j = 0; j < OPEN_MAX; j++)

if (client[j] < 0) {

client[j] = connfd; /* save descriptor */

break;

}

if (j == OPEN_MAX)

perr_exit("too many clients");

if (> maxi)

maxi = j; /* max index in client[] array */

tep.events = EPOLLIN; tep.data.fd = connfd;

res = epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &tep);

if (res == -1)

perr_exit("epoll_ctl");

}

else {

sockfd = ep[i].data.fd;

n = Read(sockfd, buf, MAXLINE);

if (n == 0) {

for (j = 0; j <= maxi; j++) {

if (client[j] == sockfd) {client[j] = -1;

break;

}

}

res = epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, NULL);

if (res == -1)

perr_exit("epoll_ctl");

Close(sockfd);

printf("client[%d] closed connection ", j);

}

else {

for (j = 0; j < n; j++)

buf[j] = toupper(buf[j]);

Writen(sockfd, buf, n);

}

}

}

}

close(listenfd);

close(efd);

return 0;

}

 

游戏开发组   供稿