最近在 看一些关于linux C 服务器编程方面的东西,随及就涉及到了大量连接的处理的问题。我 们知道在linux下我们一般用tcp/ip协议去写简单的C/S模型的代码,经常会用到select()函数。它是用来确定一个或多个套接口状态的函数。对每一个套接口,调用者可查询它的可读性、可写性及错误状态信息。用fd_set结构(可以google)来表示一组等待检查的套接口,可以用来实现多路复用的I/O模型,完成我们所谓的非阻塞模式的工作方式(就是进程或线程执行此函数时不必非要等待事件发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)这里不再赘述。
epoll与select方式的最大区别首先在于select所用到的FD_SET是有限的,他的大小是由内核中__FD_SETSIZE这个参数来定义的(默认为2048)。改变大小就需要重新编译linux内核。而epoll则不是那样,它的FD上限是最大可以打开文件的数目,这个数字一般远大于2048。你可以用cat /proc/sys/fs/file-max来查看自己机子的上线值。还有其他优点像一些什么内核微调,加速内核与用户空间的信息传递。这些都涉及到了linux内核的机制。。本人不是很懂。
下面介绍一下epoll的使用方法。
epoll首先是通过create_epoll(int maxfds)来创建一个epoll句柄。(当然最基本的先要建立socket的一系列操作),其中maxfds是epoll所支持的最大句柄数。之后在你的网络主循环里面(就是我们经常写的while(1){...}),每一次链接时间的调用epoll_wait(int epfd, epoll_event events, int max events, int timeout)来查询所有的网络接口,查看他的可读性与可写性。
其中kdpfd为用epoll_create创建之后的句柄,events是一个epoll_event*的指针,当epoll_wait这个函数操作成 功之后,epoll_events里面将储存所有的读写事件。max_events是当前需要监听的所有socket句柄数。最后一个timeout是 epoll_wait的超时,为0的时候表示马上返回,为-1的时候表示一直等下去,直到有事件范围。一般如果网络主循环是单独的线程的话,可以用-1来等,这样可以保证一些效率,如果是和主逻辑在同一个线程的话,则可以用0来保证主循环的效率。在epoll_wait()之后必需要用循环去遍历事件
for(n = 0; n < nfds; ++n) {
if(events[n].data.fd == listener) { /表示有新连接进入了,进行新连接的处理。
client = accept(listener, (struct sockaddr *) &local, &addrlen);
if(client < 0){
perror("accept");
continue;
}
setnonblocking(client); // 将新连接置于非阻塞模式,用fcntl()实现(fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)|O_NONBLOCK);
ev.events = EPOLLIN | EPOLLET; //
注意,这里的参数EPOLLIN | EPOLLET并没有设置对写socket的监听,如果有写操作的话,这个时候epoll是不会返回事件的,如果要对写操作也监听的话,应该是EPOLLIN | EPOLLOUT | EPOLLET
ev.data.fd = client;
if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0) {
// 设置好event之后,将这个新的event通过epoll_ctl加入到epoll的监听队列里面,这里用EPOLL_CTL_ADD来加一个新的 epoll事件,通过EPOLL_CTL_DEL来减少一个epoll事件,通过EPOLL_CTL_MOD来修改一个事件的监听方式。
fprintf(stderr, "epoll set insertion error: fd=%d0,
client);
return -1;
}
}
else //如果不是主socket的事件的话,则代表是一个用户socket的事件,则来处理这个事件。(比如对应的socket之间的消息传送和接受send()|recv()。。。)
handle_message(events[n].data.fd);
}
最后可以用close()去关闭epoll句柄。总共只涉及到了四个函数 epoll_create, epoll_ctl, epoll_wait和close。一般为了提高服务器的效率可以将事件的监听与处理用不同的线程去处理。这样效率会更高。
在此附上一个实例源码链接: http://zhoulifa.bokee.com/6081520.html