epoll的简单实例

7人阅读 评论(0) 收藏 举报
分类:

epoll函数的简单实例:

先了解epoll相关原理, 再分析相应实例代码

 参考《后台开发 核心技术与运用实现》


 服务端: server.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <sys/types.h>

#define IPADDRESS "127.0.0.1"
#define PORT 6666
#define MAXSIZE 1024
#define FDSIZE 100
#define LISTENQ 5 // 相应socket可以排队的最大连接数
#define EPOLLEVENTS 100

// 创建套接字并绑定
static int SocketBind(const char *ip, int port);
// IO多路复用epoll
static void DoEpoll(int listenfd);
// 事件处理函数
static void HandleEvents(int epollfd, struct epoll_event *events, int num, int listenfd, char *buf);
// 处理接受到的连接
static void HandleAccept(int epollfd, int listenfd);
// 读处理
static void DoRead(int epollfd, int fd, char *buf);
// 写处理
static void DoWrite(int epollfd, int fd, char *buf);
// 添加事件
static void AddEvent(int epollfd, int fd, int state);
// 修改事件
static void ModifyEvent(int epollfd, int fd, int state);
// 删除事件
static void DeleteEvent(int epollfd, int fd, int state);

int main(int argc, char **argv)
{
    int listenfd;
    listenfd = SocketBind(IPADDRESS, PORT);
    if (listen(listenfd, LISTENQ) == -1)
    {
        perror("listen error:");
        exit(1);
    }
    DoEpoll(listenfd);
    if (listenfd)
    close(listenfd);
    return 0;
}

static int SocketBind(const char *ip, int port)
{
    int listenfd;
    struct sockaddr_in servaddr;
    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror("socket error:");
        exit(1);
    }
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &servaddr.sin_addr);
    servaddr.sin_port = htons(port);
    if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1)
    {
        perror("bind error:");
        exit(1);
    }
    return listenfd;
}

static void DoEpoll(int listenfd)
{
    int epollfd;
    struct epoll_event events[EPOLLEVENTS];
    int ret;
    char buf[MAXSIZE];
    bzero(buf, sizeof(buf));
    // 创建一个描述符
    epollfd = epoll_create(FDSIZE);
    // 添加初始监听描述符事件
    AddEvent(epollfd, listenfd, EPOLLIN);
    while (1)
    {
        // 获取已经准备好的描述符事件
        // 永久阻塞, 直到有需要处理的事件
        // ret 为需要处理的事件数目
        // events为从内核得到事件的集合
        ret = epoll_wait(epollfd, events, EPOLLEVENTS, -1);
        HandleEvents(epollfd, events, ret, listenfd, buf);
    }
    if (epollfd)
    close(epollfd);
}

static void AddEvent(int epollfd, int fd, int state)
{
    struct epoll_event ev;
    ev.events = state;
    ev.data.fd = fd;
    epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);
}

static void ModifyEvent(int epollfd, int fd, int state)
{
    struct epoll_event ev;
    ev.events = state;
    ev.data.fd = fd;
    epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &ev);
}

static void DeleteEvent(int epollfd, int fd, int state)
{
    struct epoll_event ev;
    ev.events = state;
    ev.data.fd = fd;
    epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &ev);
}

static void HandleEvents(int epollfd, struct epoll_event *events, int num, int listenfd, char *buf)
{
    int fd;
    // 这里相对于select/poll函数的好处是不用遍历所有
    // 的fd, 只需要遍历发生变化的。
    for (int i = 0; i < num; ++i)
    {
        fd = events[i].data.fd;
        // 根据描述符的类型和事件的类型分别处理
        if (fd == listenfd && (events[i].events & EPOLLIN))
            HandleAccept(epollfd, listenfd);
        else if (events[i].events & EPOLLIN)
            DoRead(epollfd, fd, buf);
        else if (events[i].events & EPOLLOUT)
            DoWrite(epollfd, fd, buf);
    }
}

static void HandleAccept(int epollfd, int listenfd)
{
    int clientfd;
    struct sockaddr_in cliaddr;
    socklen_t cliaddrlen;
    if ((clientfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddrlen)) == -1)
        perror("accept error:");
    else
    {
        printf("accept a new client: %s:%d\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));
        // 添加新的客户描述符和事件
        AddEvent(epollfd, clientfd, EPOLLIN);
    }
}


static void DoRead(int epollfd, int fd, char *buf)
{
    int nread = read(fd, buf, MAXSIZE);
    if (nread > 0)
    {
        printf("message: %s\n", buf);
        // 将描述符对应的读事件改为写事件
        ModifyEvent(epollfd, fd, EPOLLOUT);
    }
    else
    {
        if (nread == 0)
            fprintf(stderr, "client close.\n");
        else
            perror("read error:");
        close(fd);
        DeleteEvent(epollfd, fd, EPOLLIN);

    }
}

static void DoWrite(int epollfd, int fd, char *buf)
{
    int nwrite = write(fd, buf, strlen(buf));    
    if (nwrite == -1)
    {
        perror("write error:");
        close(fd);
        DeleteEvent(epollfd, fd, EPOLLOUT);
    }
    else
        ModifyEvent(epollfd, fd, EPOLLIN);
    bzero(buf, MAXSIZE);
}


 客户端: client.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>

#define IPADDRESS "127.0.0.1"
#define SERV_PORT 6666
#define MAXSIZE 1024
#define FDSIZE 100
#define EPOLLEVENTS 100

// 连接服务端
static void HandleConnection(int sockfd);
// 事件处理函数
static void HandleEvents(int epollfd, struct epoll_event *events, int num, int sockfd, char *buf);
// 读处理
static void DoRead(int epollfd, int fd, int sockfd, char *buf);
// 写处理
static void DoWrite(int epollfd, int fd, int sockfd, char *buf);
// 添加事件
static void AddEvent(int epollfd, int fd, int state);
// 修改事件
static void ModifyEvent(int epollfd, int fd, int state);
// 删除事件
static void DeleteEvent(int epollfd, int fd, int state);

static int count = 0;

int main(int argc, char **argv)
{
    int sockfd;
    // ipv4
    sockaddr_in servaddr;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERV_PORT);
    inet_pton(AF_INET, IPADDRESS, &servaddr.sin_addr);
    connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    // 处理连接
    HandleConnection(sockfd);
    if (sockfd)
        close(sockfd);
    return 0;
}

static void HandleConnection(int sockfd)
{
    int epollfd;
    struct epoll_event events[EPOLLEVENTS];
    char buf[MAXSIZE];
    bzero(buf, MAXSIZE);
    int ret;
    epollfd = epoll_create(FDSIZE);
    // 标准输入描述符
    AddEvent(epollfd, STDIN_FILENO, EPOLLIN);
    while (1)
    {
        ret = epoll_wait(epollfd, events, EPOLLEVENTS, -1);
        HandleEvents(epollfd, events, ret, sockfd, buf);
    }
    if (epollfd)
        close(epollfd);
}

static void HandleEvents(int epollfd, struct epoll_event *events, int num, int sockfd, char *buf)
{
    int fd;
    for (int i = 0; i < num; ++i)
    {
        fd = events[i].data.fd;
        if (events[i].events & EPOLLIN)
            DoRead(epollfd, fd, sockfd, buf);
        else if (events[i].events & EPOLLOUT)
            DoWrite(epollfd, fd, sockfd, buf);
    }
}

static void DoRead(int epollfd, int fd, int sockfd, char *buf)
{
    int nread = read(fd, buf, MAXSIZE);
    if (nread > 0)
    {
        if (fd == STDIN_FILENO)
            // 如果是标准输入, 则添加一个写事件,
            // 将标准输入的内容发送给服务端
            AddEvent(epollfd, sockfd, EPOLLOUT);
        else
        {
            // 如果是从服务端读来的数据, 则删除这个读事件
            // 并且添加一个标准输入描述符
            // 将从服务端读来的数据标准输出
            DeleteEvent(epollfd, sockfd, EPOLLOUT);
            AddEvent(epollfd, STDOUT_FILENO, EPOLLOUT);
        }
    }
    else
    {
        if (nread == 0)
            fprintf(stderr, "server closed.\n");
        else
            perror("read error:");
        close(fd);
    }
}

static void DoWrite(int epollfd, int fd, int sockfd, char *buf)
{
    char temp[100];
    // 去掉buf中的'\n'或者' '
    buf[strlen(buf) - 1] = '\0';
    snprintf(temp, sizeof(temp), "%s_%02d\n", buf, count++);
    int nwrite = write(fd, temp, strlen(temp));
    if (nwrite == -1)
    {
        perror("write error:");
        close(fd);
    }
    else
    {
        if (fd == STDOUT_FILENO)
            // 删除标准输出事件
            DeleteEvent(epollfd, fd, EPOLLOUT);
        else
            // 将读事件修改为写事件
            ModifyEvent(epollfd, fd, EPOLLIN);
    }
    bzero(buf, MAXSIZE);
}

static void AddEvent(int epollfd, int fd, int state)
{
    struct epoll_event ev;
    ev.events = state;
    ev.data.fd = fd;
    epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);
}

static void ModifyEvent(int epollfd, int fd, int state)
{
    struct epoll_event ev;
    ev.events = state;
    ev.data.fd = fd;
    epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &ev);
}

static void DeleteEvent(int epollfd, int fd, int state)
{
    struct epoll_event ev;
    ev.events = state;
    ev.data.fd = fd;
    epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &ev);
}
查看评论

Linux epoll机制简单实例

server.c: #include #include #include #include #include #include #include #include #include ...
  • szkbsgy
  • szkbsgy
  • 2016-02-15 22:44:16
  • 438

epoll原理分析,代码深入分析

  • 2011年09月03日 13:34
  • 197KB
  • 下载

epoll服务示例(suse10下编译运行无问题)

  • 2014年05月16日 09:35
  • 16KB
  • 下载

epoll内核代码学习

  • 2011年06月15日 14:51
  • 127KB
  • 下载

epoll版telnet服务器

  • 2010年12月29日 23:53
  • 67KB
  • 下载

利用epoll统一调度信号、定时器和事件

利用epoll统一调度信号signal、定时器timer和事件event
  • Allan_Zeng
  • Allan_Zeng
  • 2015-09-30 16:12:46
  • 2116

epoll模型及其框架(内附epoll 线程池项目代码)

在linux的网络编程中,很长的时间都在使用select来做事件触发。在linux新的内核中,有了一种替换它的机制,就是epoll。 相比于select,epoll最大的好处在于它不会随着监听fd数目...
  • qq327767852
  • qq327767852
  • 2016-03-03 20:59:29
  • 1185

我读过最好的Epoll模型讲解

首先我们来定义流的概念,一个流可以是文件,socket,pipe等等可以进行I/O操作的内核对象。     不管是文件,还是套接字,还是管道,我们都可以把他们看作流。 ...
  • mango_song
  • mango_song
  • 2015-01-12 16:26:40
  • 15599

epoll详解(二)-- epoll工作模式

本文要点: 1. epoll 两种工作模式 2. epoll 工作模式的选择 3. epoll 编程需要注意的问题...
  • myloveqingmu
  • myloveqingmu
  • 2016-06-23 22:47:57
  • 529

网络编程4---poll与epoll的区别

之前在我的文章:网络编程1---select poll epoll 中总结了这3个函数的接口,但是对于epoll为什么比poll好,知之甚少。 今天在网上查了一些,进行了一些总结。 这篇文章总结自:...
  • u010087886
  • u010087886
  • 2016-02-26 10:10:15
  • 783
    个人资料
    持之以恒
    等级:
    访问量: 195
    积分: 87
    排名: 161万+
    文章存档
    最新评论