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);

}
相关文章
|
6天前
|
安全 Linux 虚拟化
网络名称空间在Linux虚拟化技术中的位置
网络名称空间(Network Namespaces)是Linux内核特性之一,提供了隔离网络环境的能力,使得每个网络名称空间都拥有独立的网络设备、IP地址、路由表、端口号范围以及iptables规则等。这一特性在Linux虚拟化技术中占据了核心位置🌟,它不仅为构建轻量级虚拟化解决方案(如容器📦)提供了基础支持,也在传统的虚拟机技术中发挥作用,实现资源隔离和网络虚拟化。
网络名称空间在Linux虚拟化技术中的位置
|
6天前
|
网络协议 安全 Linux
Linux网络名称空间之独立网络资源管理
Linux网络名称空间是一种强大的虚拟化技术🛠️,它允许用户创建隔离的网络环境🌐,每个环境拥有独立的网络资源和配置。这项技术对于云计算☁️、容器化应用📦和网络安全🔒等领域至关重要。本文将详细介绍在Linux网络名称空间中可以拥有的独立网络资源,并指出应用开发人员在使用时应注意的重点。
|
6天前
|
安全 网络协议 Linux
Linux网络名称空间概述
Linux网络名称空间是操作系统级别的一种虚拟化技术🔄,它允许创建隔离的网络环境🌐,使得每个环境拥有自己独立的网络资源,如IP地址📍、路由表🗺️、防火墙规则🔥等。这种技术是Linux内核功能的一部分,为不同的用户空间进程提供了一种创建和使用独立网络协议栈的方式。本文旨在全方面、多维度解释Linux网络名称空间的概念、必要性和作用。
Linux网络名称空间概述
|
14天前
|
Linux
Linux中centos桌面消失网络图标
Linux中centos桌面消失网络图标
13 0
|
4天前
|
存储 算法 Linux
【实战项目】网络编程:在Linux环境下基于opencv和socket的人脸识别系统--C++实现
【实战项目】网络编程:在Linux环境下基于opencv和socket的人脸识别系统--C++实现
17 6
|
7天前
|
网络协议 Linux
在Linux中,管理和配置网络接口
在Linux中管理网络接口涉及多个命令,如`ifconfig`(在新版本中被`ip`取代)、`ip`(用于网络设备配置)、`nmcli`(NetworkManager的CLI工具)、`nmtui`(文本界面配置)、`route/ip route`(处理路由表)、`netstat/ss`(显示网络状态)和`hostnamectl/systemctl`(主机名和服务管理)。这些命令帮助用户启动接口、设置IP地址、查看连接和路由信息。不同发行版可能有差异,建议参考相应文档。
17 4
|
10天前
|
域名解析 监控 网络协议
Linux网卡与IP地址:通往网络世界的通行证 🌐
探索Linux网卡与IP地址关系,理解网卡作为网络通信的关键。Linux网卡需配置IP地址以实现唯一标识、通信、路由、安全管理和网络服务。无IP地址时,网卡在特定情况如局域网服务、网络监控、无线认证和网络启动可有限工作,但通用功能受限。配置IP地址通常通过`ifconfig`(传统)或`ip`(现代)命令,永久配置需编辑网络配置文件。配置错误如IP冲突、子网掩码错误、默认网关和DNS配置不当可能导致服务中断、网络拥堵、安全漏洞和数据丢失。重视网络配置的正确与安全至关重要。
Linux网卡与IP地址:通往网络世界的通行证 🌐
|
19天前
|
存储 监控 网络协议
linux网络相关
在Linux中,网络管理涉及关键点如配置文件(/etc/network/interfaces, /etc/sysconfig/network-scripts/,或Systemd的`networkd`, `NetworkManager`)、接口管理命令(ifconfig, ip)、网络状态查询(ss, netstat, mtr)、配置相关命令(ifup/ifdown, hostnamectl, nmcli)、设备管理(ethtool, mii-tool)和底层网络包发送流程。常用任务包括编辑配置文件、调整IP设置、管理主机名及使用命令行工具进行网络诊断。如需深入了解特定命令或配置步骤,详细需求。
15 3
|
20天前
|
存储 Unix Linux
深入理解 Linux 系统下的关键网络接口和函数,gethostent,getaddrinfo,getnameinfo
深入理解 Linux 系统下的关键网络接口和函数,gethostent,getaddrinfo,getnameinfo
13 0
|
29天前
|
机器学习/深度学习 数据采集 人工智能
m基于深度学习网络的手势识别系统matlab仿真,包含GUI界面
m基于深度学习网络的手势识别系统matlab仿真,包含GUI界面
38 0