epoll的简单实例

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);
}
阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页