网络上所有资料都说epoll是高并发、单线程、IO重叠服用的首选架构,比select和poll性能都要好,特别是在有大量不活跃连接的情况下。具体原理就不阐述了,下面说说使用。

具有有三个函数:

#include <sys/epoll.h>

1、int epoll_create ( int size );

size是epoll要监视的fd的规模。

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

(1)epfd:epoll_create的返回值。

(2)op  指定操作类型:

EPOLL_CTL_ADD:往事件表中注册fd上的事件

EPOLL_CTL_MOD:修改fd上的注册事件

EPOLL_CTL_DEL:删除fd上的注册事件

(3)fd:要操作的文件描述符(socket)

(4)event:指定要监听fd的什么事情。它是epoll_event结构指针类型:

struct epoll_event

{

__unit32_t events;    // epoll事件

epoll_data_t data;     // 用户数据

};

events:描述事件类型。events可以是以下几个宏的集合:

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

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

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

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

EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里。

data成员:其中data.fd常用来装要操作的fd。

typedef union
epoll_data

{

void *ptr;

int
fd;

__uint32_t u32;

__uint64_t u64;

}
epoll_data_t;

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

epoll_wait的工作流程是:等待,如果有epoll事件发生立刻返回,否则等待timeout毫秒。返回时拷贝要处理的事件到events指向的数组,返回就绪的文件描述符的个数,失败时返回-1并设置errno。

timeout:指定epoll的超时时间,单位是毫秒。当timeout为-1是,epoll_wait调用将永远阻塞,直到某个事件发生。当timeout为0时,epoll_wait调用将立即返回。

maxevents:指定最多监听多少个事件。如果events指向的是20个单元的结构体数组,那么就设置maxevents为20。

events: events指向的数组中将拷贝所有就绪的事件,从内核事件表中。events的成员是struct
epoll_event类型,一般包含events(其值是如:EPOLLIN、EPOLLOUT等)。还有一个是data.fd,包含拷贝的事件对应的socket,如果是服务器监听socket,说明是有用户连接到这个socket,如果是其他已经连接好的socket,说明是有数据要发送或者接收。

如果事件数组中的数据得到处理,那么内核事件表中的事件就会被删除,下次wait就没有那些socket的事件了。

实例代码:

epoll.c

#include
<stdio.h>

#include
<sys/epoll.h>

#include
<sys/socket.h>

#include
<stdlib.h>

#include
<netinet/in.h>                 //包含sockaddr_in定义

#include
<errno.h>

#include
<string.h>                     //包含memset strncpy

int main(int
argc,char* argv[])   //主函数

{

int epfd1;int result;

int server_len,client_len;

int server_sockfd,client_sockfd;

struct sockaddr_in server_address;       //定义在
<netinet/in.h>

struct sockaddr_in client_address;

struct epoll_event ev1;

struct epoll_event ev[20];

int epollreturn;

int i,j,res;

int sockfd;

char ch = '0';

char buff[1024];

server_address.sin_family = AF_INET;

server_address.sin_addr.s_addr =
inet_addr("192.168.131.129");

server_sockfd =
socket(AF_INET,SOCK_STREAM,0);

server_address.sin_port = htons(9734);

server_len = sizeof(server_address);

client_len = sizeof(client_address);

result = bind(server_sockfd,(struct
sockaddr*)&server_address,server_len);

if(result!=0)

{

printf("bind failed\n");

exit(1);                             //在stdlib.h

}

epfd1 = epoll_create(10000);

ev1.data.fd = server_sockfd;

ev1.events = EPOLLIN;

/*

printf("%08x\n",EPOLLIN);

printf("%08x\n",EPOLLOUT);

printf("%08x\n",EPOLLPRI);

printf("%08x\n",EPOLLERR);

printf("%08x\n",EPOLLHUP);

printf("%08x\n",EPOLLET);

printf("%08x\n",EPOLLONESHOT);

*/

epoll_ctl(epfd1,EPOLL_CTL_ADD,server_sockfd,&ev1);

result = listen(server_sockfd,5);

if(result!=0)

{

printf("listen failed\n");

exit(1);

}

memset(buff,0,1024);

strncpy(buff,"this is
server",14);

for(;;)

{

epollreturn  = epoll_wait(epfd1,ev,20,4000);

printf("epollreturn is
%d\n",epollreturn);

if(epollreturn>0)

{

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

{

if(ev[i].data.fd==server_sockfd)//如果新监测到一个SOCKET用户连接到了绑定的SOCKET端口,建立新的连接。

{

client_sockfd =
accept(server_sockfd,(struct sockaddr *)&client_address,
&client_len);//没有计算client_len的值,会导致accept返回-1

printf("accept
one client,socket:%d\n",client_sockfd);

ev1.data.fd=client_sockfd;

ev1.events=EPOLLIN;

epoll_ctl(epfd1,EPOLL_CTL_ADD,client_sockfd,&ev1);

//ev1.data.fd=client_sockfd;

//ev1.events=EPOLLOUT;

//epoll_ctl(epfd1,EPOLL_CTL_ADD,client_sockfd,&ev1);
//注册

}

else
if(ev[i].events&EPOLLIN)//如果是已经连接的用户,收到数据,那么进行读入。

{

sockfd =
ev[i].data.fd;

if (sockfd < 0)

{

printf("EPOLLIN,sockfd
< 0\n");

continue;

}

res =
recv(sockfd,&ch,1,0);

if (res < 0)

{

if
(errno == ECONNRESET)

{

close(sockfd);

ev[i].data.fd
= -1;

printf("EPOLLIN,res<0,errno
== ECONNRESET\n");

}

else

printf("EPOLLIN,recv
error,res <0\n");

}

else if (res == 0)

{

close(sockfd);                    //个测试发现关闭socket,epoll队列中就不再监视这个socket了,似乎不需要删除监视

ev[i].data.fd
= -1;

printf("EPOLLIN,res
== 0\n");

ev1.data.fd=sockfd;

ev1.events=EPOLLIN;

epoll_ctl(epfd1,EPOLL_CTL_DEL,sockfd,&ev1);

}

else

{

printf("EPOLLIN,receive
one char %c,socket is %d\n",ch,sockfd);

}

ev1.data.fd=sockfd;

ev1.events=EPOLLOUT;

epoll_ctl(epfd1,EPOLL_CTL_MOD,sockfd,&ev1);

/**/

}

else
if(ev[i].events&EPOLLOUT) // 监测数据发送的原理是,对端调用recv,通知到服务器端,通知epoll,这个socket有数据要发。

{

sockfd =
ev[i].data.fd;

res =
send(sockfd,buff,102,0);

if(res==-1)

{

printf("send
error,res is %d\n",res);

close(sockfd);

ev1.data.fd=sockfd;

ev1.events=EPOLLOUT;

epoll_ctl(epfd1,EPOLL_CTL_DEL,sockfd,&ev1);

}

ev1.data.fd=sockfd;
//设置用于读操作的文件描述符

ev1.events=EPOLLIN;
//设置用于注测的读操作事件

epoll_ctl(epfd1,EPOLL_CTL_MOD,sockfd,&ev1);  //修改sockfd上要处理的事件为EPOLIN

}

}

}

}

return 0;

}

client2.c

#include
<sys/types.h>

#include
<sys/socket.h>

#include
<stdio.h>

#include
<netinet/in.h>

#include
<arpa/inet.h>

#include
<unistd.h>

#include
<stdlib.h>

#include
<string.h>                     //包含memset strncpy

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

{                                                      
//

int sockfd;                                         //

int len,i,res;

struct sockaddr_in address;

int result;

char ch = 'A';

char buff[1024];

sockfd =
socket(AF_INET,SOCK_STREAM,0);             
//奇怪,一个client运行多个版本,每次获取的居然是同一个socket。改个名字也不行

printf("socket is %d\n",sockfd);

address.sin_family = AF_INET;

address.sin_addr.s_addr =
inet_addr("192.168.131.129");

address.sin_port = htons(9734);

len = sizeof(address);

result = connect(sockfd,(struct
sockaddr*)&address,len);

if(result == -1)

{

perror("oops:client1");

exit(-1);

}

memset(buff,0,1024);

i = 0;

//for(i=0;i<10;i++)

for(;;)

{

res = send(sockfd,&ch,1,0);

if(res==-1)

{

printf("send error,res is
%d,exiting program\n",res);

close(sockfd);

return(-1);

}

/**/

i++;

memset(buff,0,102);

res = recv(sockfd,buff,102,0);

//if((res==-1)||(res==0))

if(res==-1)

{

printf("recv error,res is
%d,exiting program\n",res);

close(sockfd);

return(-1);

}

printf("socket:%d,buff is %s,i is
%d\n",sockfd,buff,i);

/**/

}

//scanf("%s",buff);

printf("exiting program\n");

close(sockfd);

return 0;

}

Linux编程之Epoll高并发的更多相关文章

  1. Linux编程之epoll

    现在有这么一个场景:我是一个很忙的大老板,我有100个手机,手机来信息了,我的秘书就会告诉我"老板,你的手机来信息了."我很生气,我的秘书就是这样子,每次手机来信息就只告诉我来信息 ...

  2. 高并发网络编程之epoll详解(转载)

    高并发网络编程之epoll详解(转载) 转载自:https://blog.csdn.net/shenya1314/article/details/73691088 在linux 没有实现epoll事件 ...

  3. Linux编程之ICMP洪水攻击

    我的上一篇文章<Linux编程之PING的实现>里使用ICMP协议实现了PING的程序,ICMP除了实现这么一个PING程序,还有哪些不为人知或者好玩的用途?这里我将介绍ICMP另一个很有 ...

  4. 高并发网络编程之epoll详解

    select.poll和epoll的区别 在linux没有实现epoll事件驱动机制之前,我们一般选择用select或者poll等IO多路复用的方法来实现并发服务程序.在大数据.高并发.集群等一些名词 ...

  5. linux/unix网络编程之epoll

    转载自 Linux epoll模型 ,这篇文章讲的非常详细! 定义: epoll是Linux内核为处理大批句柄而作改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显 ...

  6. linux学习之多高并发服务器篇(一)

    高并发服务器 高并发服务器 并发服务器开发 1.多进程并发服务器 使用多进程并发服务器时要考虑以下几点: 父最大文件描述个数(父进程中需要close关闭accept返回的新文件描述符) 系统内创建进程 ...

  7. Linux编程之UDP SOCKET全攻略

    这篇文章将对linux下udp socket编程重要知识点进行总结,无论是开发人员应知应会的,还是说udp socket的一些偏僻知识点,本文都会讲到.尽可能做到,读了一篇文章之后,大家对udp so ...

  8. Linux编程之PING的实现

    PING(Packet InterNet Groper)中文名为因特网包探索器,是用来查看网络上另一个主机系统的网络连接是否正常的一个工具.ping命令的工作原理是:向网络上的另一个主机系统发送ICM ...

  9. Linux编程之select

    select系统调用的的用途是:在一段指定的时间内,监听用户感兴趣的文件描述符上可读.可写和异常等事件. select 机制的优势 为什么会出现select模型? 先看一下下面的这句代码: int i ...

随机推荐

  1. WAKE-WIN10-SOFT-MATio

    1,matio Matio is an open-source C library for reading and writing binary MATLAB MAT files. This libr ...

  2. March 26 2017 Week 13 Sunday

    Deliver not your words by number but by weight. 言不在多,而在有物. Do more than talk, say something. I still ...

  3. fstat、stat和lstat 区别(转)

    fstat.stat和lstat 区别(转) stat系统调用系列包括了fstat.stat和lstat,它们都是用来返回“相关文件状态信息”的,三者的不同之处在于设定源文件的方式不同. 1 首先隆重 ...

  4. Linux获取系统当前时间(精确到毫秒)

    #include <stdio.h> #include <time.h> #include <sys/time.h> void sysLocalTime() { t ...

  5. 西汉姆VS利物浦,铁锤『拳』出击,打回原『菱』形

    一.铁锤,还是铁桶?   铁锤帮西汉姆今晚摆出4-2-3-1对阵红军利物浦的4-3-3 ,阿勒戴斯曾在主场2-1战胜赫尔城时被球迷嘘,嫌他的足球太过丑陋『2010年的时候贝尼特斯也曾经诟病阿勒戴斯带队 ...

  6. note04-计算机网络

    4.WLAN无线局域网(wareless local area network) IEEE802.11无线以太网协议标准 基础服务集合BSS 基站AP 服务服务集合标识SSID 即wifi名 分配系统 ...

  7. Thread control block & thread

    https://en.wikipedia.org/wiki/Thread_control_block Thread Control Block (TCB) is a data structure in ...

  8. java读取资源文件(Properties)

    四步: java代码 //new一个读取配置文件 Properties properties=new Properties(); //获取文件路径 String path=request.getSer ...

  9. 【洛谷P2577】[ZJOI2005]午餐

    午餐 题目链接 DP题都辣么毒瘤的么.. 首先,看一下题解 我们就有了思路: 贪心:显然,让吃饭慢的先打饭,sort一遍(证明?不存在的.. DP:f[i][j][k]表示前i个人,窗口1的打饭时间为 ...

  10. 【luogu T34117 打油门】 题解

    王强怎么这么强啊 王强太强了 二维树状数组 #include <cstdio> #include <cstring> #include <iostream> #in ...