epoll的使用

以下内容是参考《linux/unix系统编程手册》对epoll的一个个人总结。

一、epoll的优点

   同I/O多路复用和信号驱动I/O一样,linuxepoll API可以检查多个文件描述符上的I/O就绪状态。epoll API的主要优点

1.当有大量的文件描述符需要检查时,epoll的性能延展性比select()和epoll(高很多)

2.epoll API既支持水平触发也支持边缘触发,与之相反,selectpoll只支持水平触发,而信号驱动I/O只支持边缘触发

3.可以避免复杂的信号处理流程(比如信号队列溢出时的处理)

4.灵活性高,可以指定我们希望检查的时间类型

二、epoll系统调用组成

1.epoll_create()创建一个epoll实例,返回代表该实例的文件描述符

2.epoll_ctl()操作同epoll实例相关联的兴趣列表

3.epoll_wait()返回与epoll实例相关联的就绪列表中的成员

#include <sys/epoll.h>


int epoll_create(int size);

功能:创建一个新的epoll实例,其对应的兴趣列表初始化为空

参数:size 想要检查文件描述符的个数(linux2.6.8之后该参数不再使用)

返回值:成功文件描述符,失败-1

#include <sys/epoll.h>

 

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

功能:修改epoll的兴趣列表

参数:epfd  epoll_create的返回值

      fd    要修改的文件描述符(可以是无名管道、有名管道、套接字、消息队列、终端、设备等,但是不能是普通文件或目录的文件描述符)

      op

  EPOLL_CTL_ADD  添加fd到兴趣列表

  EPOLL_CTL_MOD 修改已经注册的fd的监听事件;

  EPOLL_CTL_DEL epfd中删除一个fd

  typedef union epoll_data { 
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
  } epoll_data_t; 
  struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
  };

  常用的事件类型:
  EPOLLIN :表示文件描述符可以读;
  EPOLLOUT:表示文件描述符可以写;
  EPOLLPRI:表示文件描述符有紧急的数据可读
  EPOLLERR:表示文件描述符发生错误;
  EPOLLHUP:表示文件描述符被挂断;
  EPOLLET 表示文件描述符有事件发生;

返回值:成功0 失败-1

#include <sys/epoll.h>

 

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

功能:返回epoll实例中处于就绪态的文件描述符信息,单个epoll_wait()调用能返回多个就绪态文件描述符的信息。

参数:epfd  epoll实例

      events  存放文件描述符的信息

      maxevents  文件描述符的最大个数

      timeout   -1  阻塞等待

                 0  非阻塞

                >0  阻塞的最大时间

返回值:成功0   失败-1

 

三、网络中的应用实例

1.net.h

#ifndef _NET_H_

#define _NET_H_

 

#include <stdio.h>

#include <unistd.h>

#include <errno.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <stdlib.h>

#include <fcntl.h>

#include <strings.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <string.h>

#include <signal.h>

#include <sys/time.h>

#include <sys/epoll.h>

 

#include <pthread.h>

#define MAX_EVENTS 500

 

#endif

2.server.c

#include "head.h"

 

int sockfd, acceptfd,listenfd;

struct sockaddr_in serveraddr, clientaddr;

socklen_t len = sizeof(serveraddr);

struct epoll_event ev, events[MAX_EVENTS];

int rv,i;

int epollfd,fds;

 

int tcp_create_socket(void)

{

//1、设定基于TCP通信的标准

sockfd = socket(AF_INET, SOCK_STREAM, 0);

if(sockfd < 0)

{

perror("fail to socket");

return -1;

}

bzero(&serveraddr, len);

serveraddr.sin_family = AF_INET;

serveraddr.sin_port = htons(atoi("8000"));//端口号

serveraddr.sin_addr.s_addr = inet_addr("192.168.8.199");  //IP地址

 

//2、绑定IP地址和端口号

if(bind(sockfd, (struct sockaddr*)&serveraddr, len) < 0)

{

perror("fail to bind");

close(sockfd);

return -1;

}

 

//3、设置监听描述符的个数

if(listen(sockfd, 5) < 0)

{

perror("fail to listen");

close(sockfd);

return -1;

}

 

return sockfd;

}

 

int mz_process_data(int acceptfd)//数据处理

{

int bytes = 0;

char buf[100];

 

bytes = recv(acceptfd, buf, 100, 0);

if(bytes < 0)

{

perror("recv error");

return -1;

}

 

if(bytes == 0) //说明客户端放弃链接

{

return -2;

}

printf("client:%s\n", buf);

return 0;

}

 

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

{

epollfd = epoll_create(MAX_EVENTS);//创建对象

        if(epollfd < 0)

{

perror("fail to epoll!");

return -1;

}

 

listenfd = tcp_create_socket();

 

fcntl(listenfd,F_SETFL, O_NONBLOCK);//设置非阻塞

 

ev.data.fd = listenfd;

ev.events = EPOLLIN;

rv = epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &ev);//加入监听事件

if (rv < 0)

{

perror("epoll_ctl err:");

return -1;

}

 

         while(1)

{

fds = epoll_wait(epollfd, events, MAX_EVENTS, -1);

if(fds < 0)

{

           perror("epoll_wait error");

    return -1;

}

//轮寻被激活的套接子

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

{

if(events[i].data.fd == listenfd) //判断是否是监听的对象(描述符)

{

acceptfd = accept(listenfd, (struct sockaddr*)&clientaddr, &len);

if(acceptfd < 0)

{

perror("accept error");

continue;

}

                

ev.data.fd = acceptfd;

ev.events = EPOLLIN | EPOLLET;

//将链接的客户端添加到关注的事件中去

epoll_ctl(epollfd, EPOLL_CTL_ADD, acceptfd, &ev);

                                 continue;

}

else

{

rv = mz_process_data(events[i].data.fd);

if(rv == -2) //客户端放弃链接,将描述符从事件中清除

{

epoll_ctl(epollfd, EPOLL_CTL_DEL, events[i].data.fd, &ev);

close(events[i].data.fd);

continue;

}

}

}

}

 

return 0;

}

3.client.c

#include "head.h"

 

#define N 32

 

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

{

int sockfd;

struct sockaddr_in serveraddr;

socklen_t len = sizeof(serveraddr);

char buf[N] = {0};

//1、设定基于TCP通信的标准

sockfd = socket(AF_INET, SOCK_STREAM, 0);

if(sockfd < 0)

{

perror("fail to socket");

return -1;

}

 

bzero(&serveraddr, len);

serveraddr.sin_family = AF_INET;

serveraddr.sin_port = htons(atoi("8000"));//端口号

serveraddr.sin_addr.s_addr = inet_addr("192.168.8.199");  //IP地址

 

//2、绑定IP地址和端口号

if(connect(sockfd, (struct sockaddr*)&serveraddr, len) < 0)

{

perror("fail to connect");

close(sockfd);

return -1;

}

//客户端用来发送数据

while(1)

{

fgets(buf, N, stdin);

buf[strlen(buf) - 1] = '\0';

send(sockfd, buf, N, 0);

if(strncmp(buf, "quit", 4) == 0)

{

break;

}

}

close(sockfd);

return 0;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值