epoll 模型
body, table{font-family: 微软雅黑; font-size: 10pt}
table{border-collapse: collapse; border: solid gray; border-width: 2px 0 2px 0;}
th{border: 1px solid gray; padding: 4px; background-color: #DDD;}
td{border: 1px solid gray; padding: 4px;}
tr:nth-child(2n){background-color: #f8f8f8;}
在 linux 的网络编程中,很长的时间都在使用 select 来做事件触发。在 linux 新的内核中,有了一种替换它的机制,就是 epoll。相比于 select, epoll 最大的好处在于它不会随着监听 fd 数目的增长而降低效率。因为在内核中的 select 实现中,它是采用轮询来处理的,轮询的 fd 数目越多,自然耗时越多。并且,在 linux/posix_types.h 头文件有这样的声明:
#define __FD_SETSIZE 1024
表示 select 最多同时监听 1024 个 fd,当然,可以通过修改头文件再重编译内核来扩大这个数目,但这似乎并不治本。
|
||||
epoll 的接口非常简单,一共就三个函数:效率稳定,不会随着监控的描述符增多而减小 | ||||
1. int epoll_create(int size);
创建一个 epoll 的句柄, size 用来告诉内核这个监听的数目一共有多大。(epoll模型对监控的描述符没有限制,写什么都无所谓,只要不写0就好)这个参数不同于select()中的第一个参数,给出最大监听的 fd+1 的值。需要注意的是,当创建好 epoll 句柄后,它就是会占用一个 fd 值,在 linux 下如果查看/proc/进程 id/fd/,是能够看到这个 fd 的,所以在使用完 epoll 后,必须调用 close()关闭,否则可能导致 fd 被耗尽。
|
||||
2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll 的事件注册函数,它不同于 select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。//成功返回0,失败返回-1
第一个参数是 epoll_create() 的返回值,
第二个参数表示动作,用三个宏来表示:
EPOLL_CTL_ADD:注册新的 fd 到 epfd 中;
EPOLL_CTL_MOD:修改已经注册的 fd 的监听事件;
EPOLL_CTL_DEL:从 epfd 中删除一个 fd;(解注册)
第三个参数是需要监听的 fd,
第四个参数是告诉内核需要监听什么事, struct epoll_event 结构如下:
|
||||
3. int epoll_wait(int epfd, struct epoll_event *events, int maxevents,int timeout);
等待事件的产生,类似于 select()调用。参数 events 用来从内核得到事件的集合(所以每次使用前都要清空,这里相当于以前用的select的rdset集合), maxevents 告之内核这个 events 有多大,这个 maxevents 的值不能大于创建 epoll_create()时的 size,参数 timeout 是超时时间(毫秒,0 会立即返回,-1 将不确定,也有说法说是永久阻塞;是一个相对时间)。该函数返回需要处理的事件数目,如返回 0 表示已超时。有描述符可读,主动通知 epoll_wait()
|
func.h
#include <sys/types.h>
#include <sys/socket.h>
|
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
|
#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <unistd.h>
|
epoll_tcp_server.c | epoll_tcp_client.c |
#include "func.h"
#define NUM 10
int main(int argc,char* argv[])
{
if(argc!=3)
{
printf("error args\n");
return -1;
}
int sfd=socket(AF_INET,SOCK_STREAM,0);
if(-1==sfd)
{
perror("socket");
return -1;
}
struct sockaddr_in ser;
memset(&ser,0,sizeof(ser));
ser.sin_family=AF_INET;
ser.sin_port=htons(atoi(argv[2]));
ser.sin_addr.s_addr=inet_addr(argv[1]);
int ret;
ret=bind(sfd,(struct sockaddr*)&ser,sizeof(struct sockaddr));
if(-1==ret)
{
perror("bind");
return -1;
}
ret=listen(sfd,NUM);
if(-1==ret)
{
perror("listen");
return -1;
}
int epfd=epoll_create(1); //创建一个句柄,参数只要不是0就OK
struct epoll_event event,evs[NUM+1];
//第二个数组用来传参给epoll_wait(),得到哪个描述符有输入
event.events=EPOLLIN; //注册事件,多个操作要用或操作
event.data.fd=sfd; //注册要监听的描述符
ret=epoll_ctl(epfd,EPOLL_CTL_ADD,sfd,&event);
if(-1==ret)
{
perror("epoll_ctl");
return -1;
}
event.events=EPOLLIN; //注册标准输入
event.data.fd=0;
ret=epoll_ctl(epfd,EPOLL_CTL_ADD,0,&event);
if(-1==ret)
{
perror("epoll_ctl");
return -1;
}
int i;
int new_fd;
char buf[128];
int n;
while(1)
{
memset(evs,0,sizeof(evs));
ret=epoll_wait(epfd,evs,NUM+2,-1);
if(ret >0)
{
for(i=0;i<ret;i++)
{
if(evs[i].events == EPOLLIN && evs[i].data.fd == sfd)
{
new_fd=accept(sfd,NULL,NULL);
printf("accept newfd =%d\n",newfd);
event.events=EPOLLIN;
event.data.fd=new_fd;
epoll_ctl(epfd,EPOLL_CTL_ADD,new_fd,&event);
}
if(evs[i].events == EPOLLIN && evs[i].data.fd == 0)
{
memset(buf,0,sizeof(buf));
n=read(0,buf,sizeof(buf));
if(n>0)
{
send(new_fd,buf,strlen(buf)-1,0);
}else if(n==0)
{
printf("bye\n");
event.events=EPOLLIN;
event.data.fd=new_fd;
epoll_ctl(epfd,EPOLL_CTL_DEL,new_fd,&event);
close(new_fd);
}
}
if(evs[i].events == EPOLLIN && evs[i].data.fd == new_fd)
{
memset(buf,0,sizeof(buf));
n=recv(new_fd,buf,sizeof(buf),0);
if(n>0)
{
printf("recv client buf =%s\n",buf);
}else if(n==0){
printf("bye\n");
event.events=EPOLLIN;
event.data.fd=new_fd;
epoll_ctl(epfd,EPOLL_CTL_DEL,new_fd,&event);
close(new_fd);
}
}
}
}
}
return 0;
}
|
#include "func.h"
int main(int argc,char** argv)
{
if(argc !=3)
{
printf("error args\n");
return -1;
}
int sfd=socket(AF_INET,SOCK_STREAM,0);
if(-1==sfd)
{
perror("socket");
return -1;
}
struct sockaddr_in ser;
memset(&ser,0,sizeof(ser));
ser.sin_family=AF_INET;
ser.sin_port=htons(atoi(argv[2])); //一定要用htons
ser.sin_addr.s_addr=inet_addr(argv[1]);
int ret;
ret=connect(sfd,(struct sockaddr*)&ser,sizeof(struct sockaddr));
if(-1==ret)
{
perror("connect");
return -1;
}
int epfd=epoll_create(1);
struct epoll_event event,evs[2];
event.events=EPOLLIN;
event.data.fd=sfd;
ret=epoll_ctl(epfd,EPOLL_CTL_ADD,sfd,&event);
if(-1==ret)
{
perror("epoll_ctl");
return -1;
}
event.events=EPOLLIN;
event.data.fd=0;
ret=epoll_ctl(epfd,EPOLL_CTL_ADD,0,&event);
if(-1==ret)
{
perror("epoll_ctl");
return -1;
}
int i;
char buf[128];
int n;
while(1)
{
memset(evs,0,sizeof(evs));
ret=epoll_wait(epfd,evs,2,-1);
if(ret>0)
{
for(i=0;i<ret;i++)
{
if(evs[i].events == EPOLLIN && evs[i].data.fd == 0)
{
memset(buf,0,sizeof(buf));
n=read(0,buf,sizeof(buf));
if(n==0)
{
printf("bye\n");
close(sfd);
return 0;
}
n=send(sfd,buf,strlen(buf)-1,0);
if(-1==n)
{
perror("send");
return -1;
}
}
if(evs[i].events == EPOLLIN && evs[i].data.fd == sfd)
{
memset(buf,0,sizeof(buf));
n=recv(sfd,buf,sizeof(buf),0);
if(n > 0)
{
printf("recv form server buf =%s\n",bu f);
}else if(n==0)
{
printf("bye\n");
close(sfd);
return 0;
}
}
}
}
}
return 0;
}
|
epoll 模型的更多相关文章
- Epoll模型详解
Linux 2.6内核中提高网络I/O性能的新方法-epoll I/O多路复用技术在比较多的TCP网络服务器中有使用,即比较多的用到select函数. 1.为什么select落后 首先,在Lin ...
- 【转】select和epoll模型的差异
http://www.cppblog.com/converse/archive/2008/10/12/63836.html epoll为什么这么快 epoll是多路复用IO(I/O Multiplex ...
- linux epoll模型
原文:http://yjtjh.blog.51cto.com/1060831/294119 Linux I/O多路复用技术在比较多的TCP网络服务器中有使用,即比较多的用到select函数.Linux ...
- Linux网络服务器epoll模型的socket通讯的实现(一)
准备写一个网络游戏的服务器的通讯模块,参考网上看到的一些代码,在linux下面实现一个多线程的epoll模型的socket通讯的代码,以下是第一部分多线程的切换代码: 1 #include <s ...
- (OK) Linux epoll模型—socket epoll server client chat
http://www.cnblogs.com/venow/archive/2012/11/30/2790031.html http://blog.csdn.net/denkensk/article/d ...
- nginx中的epoll模型
要了解epoll模型,就要一个一个知识点由浅至深地去探索. 1.IO复用技术 IO流请求操作系统内核,有串行处理和并行处理两种概念. 串行处理是前面一个操作处理地时候,后面的所有操作都需要等待.因此, ...
- select 和epoll模型区别
1.select 和epoll模型区别 1.1.网络IO模型概述 通常来说,网络IO可以抽象成用户态和内核态之间的数据交换.一次网络数据读取操作(read),可以拆分成两个步骤:1)网卡驱动等待数据准 ...
- Epoll模型
Epoll模型 相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率.因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多.并且,在l ...
- select、poll、epoll模型对比
select.poll.epoll模型对比 先说Select: 1.Socket数量限制:该模式可操作的Socket数由FD_SETSIZE决定,内核默认32*32=1024. ...
随机推荐
- 寻找最小(最大)的k个数
题目描述:输入n个整数,输出其中最小的k个元素. 例如:输入1,2,3,4,5,6,7,8这8个数字,则最小的4个数字为1,2,3,4. 思路1:最容易想到的方法:先对这个序列从小到大排序,然后输出前 ...
- CCScene,CCLayer,CCSprite,CCDirector
一.CCScene : 游戏中不同的画面可以用不同的场景展示出来,大致的可以分为以下的几类场景: 1. 展示类场景.游戏开场画面,游戏简介,胜利以及失败提示,帮助. 2. 选择类场景.主菜单,游戏设置 ...
- maven解决“Could not calculate build plan”问题
错误提示如下:(eclipse+maven) Could not calculate build plan: Failure to transfer org.apache.maven.plugins: ...
- Virtualbox中win7虚拟机中U盘不可用问题的解决
Virtualbox版本是5.0.0,主机运行多是Ubuntu12.04 LTS,虚拟机是Win7 X64.起初Win7正常运行,Virtualbox的增强功能已安装.下面是如何一步一步解决U盘不可用 ...
- Java 对比Vector、ArrayList、LinkedList
①引言 在日常生活中能高效的管理和操作数据是非常重要的.Java提供了强大的集合框架,大大提高了开发者的生产力,今天就了解一下有关集合框架方面的问题. Vector.ArrayList.LinkedL ...
- SQL优化之limit 1
在某些情况下,如果明知道查询结果只有一个,SQL语句中使用LIMIT 1会提高查询效率. 例如下面的用户表(主键id,邮箱,密码): create table t_user( id int prim ...
- HashMap 遍历的两种方式及性能比较
HashMap 是Java开发中经常使用的数据结构.相信HashMap 的基本用法你已经很熟悉了.那么我们该如何遍历HashMap 呢?哪种遍历方式的性能更好呢?本篇文章来为你解决这个疑惑. 一.Ha ...
- 【javascript】数据结构-队列
<!DOCTYPE html> <html> <head> <title>queue</title> <meta charset=&q ...
- 日志自定义Tag
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentHashMap; /** * Crea ...
- nodejs v8引擎
Node.js 线程你理解的可能是错的 本文代码运行环境 系统:MacOS High Sierra Node.js:10.3.0 复制代码 Node.js是单线程的,那么Node.js启动后线程数是1 ...