Linux网络编程之epoll

简介:

epoll多路复用非阻塞模型

epoll多路复用技术相比select和poll更加高效,相比select和poll每次都轮询O(n),epoll每次返回k个有事件发生的fd,所以是O(k)的复杂度,或者说O(1)。epoll分为水平触发(LT)和垂直触发(ET),这两种方式下对fd的读写是很不一样的,这也是epoll编程的难点,现在很多网络库都是优先提供epoll作为多路复用的,如libev/libevent/muduo/boost.asio,还有一些组件如beanstalkd/nginx…


epoll_create(<#(int)__size#>)
epoll_ctl(<#(int)__epfd#>, <#(int)__op#>, <#(int)__fd#>, <#(struct epoll_event*)__event#>)\
epoll_wait(<#(int)__epfd#>, <#(struct epoll_event*)__events#>, <#(int)__maxevents#>, <#(int)__timeout#>)


//epoll-nonblocking LT
#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#define READ_BUF_SIZE 20*1024*1024
#define WRITE_BUF_SIZE 20*1024*1024
#define CHUNCK_SIZE 2*1024*1024
#define KEEP_ALIVE 0
#define MAX_EVENTS 2048

struct epoll_fd_state {
    char readbuf[READ_BUF_SIZE];
    char writebuf[WRITE_BUF_SIZE];
    ssize_t readlen;
    ssize_t write_pos;
    ssize_t write_upto;
    int writing;
};

void run();

int main(int argc, char *argv[])
{
    run();
    return 0;
}

struct epoll_fd_state *alloc_epoll_fd_state()
{
    struct epoll_fd_state *p = (struct epoll_fd_state *)malloc(sizeof(struct epoll_fd_state));
    if (!p){
        perror("error alloc_epoll_fd_state");
        return NULL;
    }
    p->readlen = p->write_upto = p->write_pos = p->writing = 0;
    return p;
}

void free_epoll_fd_state(struct epoll_fd_state *p)
{
    free(p);
    p = NULL;
}

//handle read event
int do_read(struct epoll_event ev, struct epoll_fd_state *state)
{
    ssize_t result;
    char buf[CHUNCK_SIZE];
    while(1){
        result = recv(ev.data.fd, buf, sizeof(buf), 0);

        if (result<=0)
            break;
        int i;
        for (i = 0; i < result; ++i) {
            if (state->readlen < sizeof(state->readbuf)){
                state->readbuf[state->readlen++] = buf[i];
            }
            printf("%c",buf[i]);
            fflush(stdout);
            //read until '\n'
            /*
             * todo: handle the readbuffer for http
             *
             */
            /*if (buf[i]=='\n'){
                state->writing = 1;
                state->written_upto = state->readlen;
                pfd->events = POLLOUT;//register write event
                break;
            }*/
        }

    }
    //change state to write
    state->writing = 1;
    state->write_upto = state->readlen;

    printf("readlen result:%d\n",(int)result);
    fflush(stdout);

    if (result==0)
        return 1;
    if (result<0){
        if (errno== EAGAIN)
            return 0;
        else
            return -1;
    }
    return 0;
}

//handle write event
int do_write(struct epoll_event ev, struct epoll_fd_state *state) {
    ssize_t result;
    while (state->write_pos < state->write_upto) {
        result = send(ev.data.fd, state->readbuf + state->write_pos, CHUNCK_SIZE, 0);
        if (result <= 0)
            break;
        state->write_pos += result;
    }

    if (state->write_pos == state->write_upto)
        state->write_pos = state->write_upto = state->readlen = 0;
    state->writing = 0;

    printf("writelen result:%d",(int)result);
    fflush(stdout);

    if (result == 0)
        return 1;
    if (result < 0) {
        if (errno == EAGAIN)
            return 0;
        else
            return -1;
    }
    return 0;
}

int create_server_socket()
{
    int fd;
    int reuse = 1;
    if ((fd= socket(AF_INET, SOCK_STREAM, 0))<0){
        perror("error create socket");
        return fd;
    }
    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))<0){
        perror("error setsockopt");
    }
    return fd;
}

void set_socket_nonblocking(int fd)
{
    if ((fd, F_SETFL, O_NONBLOCK)<0){
        perror("error set nonblocking");
        exit(EXIT_FAILURE);
    }
}

void do_epoll(int serverfd)
{
    int epollfd = epoll_create(2048);
    if (epollfd<0){
        perror("error epoll_create");
        exit(EXIT_FAILURE);
    }

    struct epoll_event ev;
    ev.data.fd = serverfd;
    ev.events = EPOLLIN;

    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, serverfd, &ev)<0){
        perror("error epoll_ctl");
        exit(EXIT_FAILURE);
    }

    struct epoll_event events[MAX_EVENTS];
    struct epoll_fd_state *fds_state[MAX_EVENTS];

    int epoll_ret;
    int clientfd;
    //epoll loop
    while(1){

        epoll_ret = epoll_wait(epollfd, events, MAX_EVENTS, 0);

        if (epoll_ret<0){
            perror("error epoll_wait");
            exit(EXIT_FAILURE);
        }
        int i,j;

        //check writing state
        for (j = 0; j < epoll_ret; ++j) {
            if (events[j].data.fd!=serverfd && fds_state[j]&&fds_state[j]->writing==1){
                /*printf("write ready:%d",events[j].data.fd);
                fflush(stdout);*/
                ev.data.fd = events[j].data.fd;
                ev.events = EPOLLOUT;
                if (epoll_ctl(epollfd, EPOLL_CTL_MOD, events[j].data.fd, &ev)<0){
                    perror("error epoll_ctl add epollout");
                    exit(EXIT_FAILURE);
                }
            }
        }

        //handle server and client sock events
        for (i = 0; i < epoll_ret; ++i) {
            int flag = 0;
            //server socket reay?
            if (events[i].data.fd==serverfd){

                struct sockaddr_in client;
                socklen_t slen = sizeof(client);
                if ((events[i].events& EPOLLIN) == EPOLLIN){

                    clientfd = accept(events[i].data.fd, (struct sockaddr *)&client, &slen);
                    if (clientfd<0){
                        perror("error accept");
                        exit(EXIT_FAILURE);
                    }
                    set_socket_nonblocking(clientfd);
                    ev.data.fd = clientfd;
                    ev.events = EPOLLIN;
                    if(epoll_ctl(epollfd, EPOLL_CTL_ADD, clientfd, &ev)<0){
                        perror("error epoll_ctl client");
                        exit(EXIT_FAILURE);
                    }

                    struct epoll_fd_state *temp = alloc_epoll_fd_state();
                    if(!temp){
                        exit(EXIT_FAILURE);
                    }
                    fds_state[i] = temp;
                }
            }else{
                if ((events[i].events& EPOLLIN)== EPOLLIN){

                    fprintf(stdout, "in do_read\n");
                    fflush(stdout);
                    flag = do_read(events[i], fds_state[i]);
                    printf("read flag:%d\n",flag);
                    fflush(stdout);
                }

                if (flag==0&&((events[i].events==EPOLLOUT)== EPOLLOUT)){
                    fprintf(stdout, "in do_write\n");

                    fflush(stdout);
                    flag = do_write(events[i], fds_state[i]);
                    if (!KEEP_ALIVE){
                        free_epoll_fd_state(fds_state[i]);
                        fds_state[i] = NULL;
                        if(epoll_ctl(epollfd, EPOLL_CTL_DEL, events[i].data.fd, events)<0){
                            perror("error epoll_ctl delete event");
                            exit(EXIT_FAILURE);
                        }
                        close(events[i].data.fd);
                    }
                }

            }

            if (flag){
                fprintf(stdout, "in error handle\n");
                printf("read error flag:%d\n",flag);
                fflush(stdout);
                free_epoll_fd_state(fds_state[i]);
                fds_state[i] = NULL;
                close(events[i].data.fd);
            }
        }

    }

}

void run()
{
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = 0;
    sin.sin_port = htons(8000);

    int serverfd = create_server_socket();
    set_socket_nonblocking(serverfd);
    if (serverfd<0){
        exit(EXIT_FAILURE);
    }

    if (bind(serverfd, (struct sockaddr *)&sin, sizeof(sin))<0){
        perror("error bind");
        exit(EXIT_FAILURE);
    }

    if (listen(serverfd, 20)<0){
        perror("error listen");
        exit(EXIT_FAILURE);
    }
    do_epoll(serverfd);

}
相关文章
|
8天前
|
监控 安全 Linux
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景,包括 ping(测试连通性)、traceroute(跟踪路由路径)、netstat(显示网络连接信息)、nmap(网络扫描)、ifconfig 和 ip(网络接口配置)。掌握这些命令有助于高效诊断和解决网络问题,保障网络稳定运行。
26 2
|
2月前
|
安全 Linux 网络安全
Web安全-Linux网络协议
Web安全-Linux网络协议
76 4
|
26天前
|
运维 监控 网络协议
|
21天前
|
存储 JSON Java
细谈 Linux 中的多路复用epoll
大家好,我是 V 哥。`epoll` 是 Linux 中的一种高效多路复用机制,用于处理大量文件描述符(FD)事件。相比 `select` 和 `poll`,`epoll` 具有更高的性能和可扩展性,特别适用于高并发服务器。`epoll` 通过红黑树管理和就绪队列分离事件,实现高效的事件处理。本文介绍了 `epoll` 的核心数据结构、操作接口、触发模式以及优缺点,并通过 Java NIO 的 `Selector` 类展示了如何在高并发场景中使用多路复用。希望对大家有所帮助,欢迎关注威哥爱编程,一起学习进步。
|
30天前
|
Ubuntu Linux 虚拟化
Linux虚拟机网络配置
【10月更文挑战第25天】在 Linux 虚拟机中,网络配置是实现虚拟机与外部网络通信的关键步骤。本文介绍了四种常见的网络配置方式:桥接模式、NAT 模式、仅主机模式和自定义网络模式,每种模式都详细说明了其原理和配置步骤。通过这些配置,用户可以根据实际需求选择合适的网络模式,确保虚拟机能够顺利地进行网络通信。
|
1月前
|
网络协议 安全 Ubuntu
Linux中网络连接问题
【10月更文挑战第3天】
32 1
|
1月前
|
Linux C++
Linux C/C++之IO多路复用(poll,epoll)
这篇文章详细介绍了Linux下C/C++编程中IO多路复用的两种机制:poll和epoll,包括它们的比较、编程模型、函数原型以及如何使用这些机制实现服务器端和客户端之间的多个连接。
24 0
Linux C/C++之IO多路复用(poll,epoll)
|
1月前
|
监控 Linux 测试技术
Linux系统命令与网络,磁盘和日志监控总结
Linux系统命令与网络,磁盘和日志监控总结
55 0
|
1月前
|
监控 Linux 测试技术
Linux系统命令与网络,磁盘和日志监控三
Linux系统命令与网络,磁盘和日志监控三
38 0
|
2月前
|
网络协议 Linux
Linux 网络配置
了解基本命令与权限后,如何让Linux系统联网?可通过编辑`/etc/sysconfig/network-scripts/`下的`ifcfg-ethX`文件配置网卡,其中`ethX`代表第X块网卡。对于DHCP自动获取或静态IP,需设置`BOOTPROTO`参数,并指定IP、子网掩码和网关等。配置完成后,运行`/etc/init.d/network restart`重启网络。DNS可在`/etc/resolv.conf`中设置,添加`nameserver`行即可,无需重启网卡。配置好后,可用`ifconfig`查看IP信息,并通过远程工具如SecureCRT连接服务器。
74 0
下一篇
无影云桌面