高性能网络编程 - epoll机制

select系统调用不仅对描述符的数量有限制,而且在高并发的情况下,哪怕只有一个活跃的套接字,也要轮询全部的fd set,而epoll采用回调的事件通知机制,只需要处理活跃的套接字。比如Nginx服务器采用的就是epoll,下面这个程序(当接收到大于10B的数据时)展示了epoll在边沿触发和电平触发的不同表现,在edge-trigger模式下,需要我们的程序一次将这次的事情处理完成(比如把数据全部读取),因为这个事件下次不会加到内核事件注册表中了,相反level-trigger模式下就跟我们的处理程序有关,如果本次没有读取出全部的数据,下次这个事情仍然会被注册的。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <errno.h>
#include <libgen.h>

#define MAXEVENTS 1024
#define BUF_SIZE 10

//set this descriptor non blocking
static int setnonblocking(int sfd){
    int flags, s;

    flags = fcntl(sfd, F_GETFL, 0);
    if (flags == -1) {
        perror ("fcntl");
        return -1;
    }   

    flags |= O_NONBLOCK;
    s = fcntl(sfd, F_SETFL, flags);
    if (s == -1) {
        perror ("fcntl");
        return -1;
    }   

    return 0;
}

//
static int create_and_bind (char *port){
    struct addrinfo hints;
    struct addrinfo *result, *rp;
    int s, sfd;

    memset (&hints, 0, sizeof (struct addrinfo));
    hints.ai_family = AF_UNSPEC;     //IPv4 IPv6通用
    hints.ai_socktype = SOCK_STREAM; /* We want a TCP socket */
    hints.ai_flags = AI_PASSIVE;     //监听套接字;

    //根据暗示得到所有可用的addrinfo
    s = getaddrinfo (NULL, port, &hints, &result);
    if (s != 0){
        fprintf (stderr, "getaddrinfo: %s\n", gai_strerror (s));
        return -1;
    }
    //use the first addr to create socket
    for (rp = result; rp != NULL; rp = rp->ai_next) {
        sfd = socket (rp->ai_family, rp->ai_socktype, rp->ai_protocol);
        if (sfd == -1)
            continue;

        s = bind (sfd, rp->ai_addr, rp->ai_addrlen);
        if (s == 0) {
            //bind success
            break;
        }

        close (sfd);//if bind failed we close this socket
    }

  if (rp == NULL) {
        fprintf (stderr, "Could not bind\n");
        return -1;
  }

  freeaddrinfo (result);

  return sfd;
}

// add EPOLLIN event of fd to the epollfd
void addfd(int epollfd, int fd, int enable_et){
    struct epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN;
    if(enable_et)
        event.events |= EPOLLET;
    epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
    setnonblocking(fd);
}

//the level-trigger logic
void lt(struct epoll_event *events, int number, int epollfd, int listenfd){
    char buf[BUF_SIZE];
    int i;
    for(i = 0; i < number; i++){
        int sockfd = events[i].data.fd;
        //new client connection
        if(sockfd == listenfd){
            struct sockaddr_in client_addr;
            socklen_t client_addrlen = sizeof(client_addr);
            int connfd = accept(listenfd, (struct sockaddr *)&client_addr, &client_addrlen);
            if(connfd < 0){
                perror("accept failed..");
                exit(-1);
            }
            //register this event
            addfd(epollfd, connfd, 0);
        }else if(events[i].events & EPOLLIN){
            //if there exists data unreaded , this code will invoke
            printf("sockefd %d read event trigger once\n", sockfd);
            int ret = recv(sockfd, buf, BUF_SIZE, 0);
            if(ret <= 0){
                printf("some read error or client closed.\n");
                close(sockfd); // will unregister
                continue;
            }
            printf("get %d bytes from client: %s\n", ret, buf);
        }else{
            // what we are not interested
            printf("something else happened\n");
        }
    }
}

//the edge-trigger logic
void et(struct epoll_event *events, int number, int epollfd, int listenfd){
    char buf[BUF_SIZE];
    int i;
    for(i = 0; i < number; i++){
        int sockfd = events[i].data.fd;
        //new client connection
        if(sockfd == listenfd){
            struct sockaddr_in client_addr;
            socklen_t client_addrlen = sizeof(client_addr);
            int connfd = accept(listenfd, (struct sockaddr *)&client_addr, &client_addrlen);
            if(connfd < 0){
                perror("accept failed..");
                exit(-1);
            }
            //register this event and set edge trigger mode
            addfd(epollfd, connfd, 1);
        }else if(events[i].events & EPOLLIN){
            //in this mode , we should read all the data out once
            printf("sockefd %d read event trigger once\n", sockfd);
            while(1){
                memset(buf, 0, sizeof(buf));
                int ret = recv(sockfd, buf, BUF_SIZE, 0);
                if(ret < 0){
                    //for the nonblocking IO,the following errors indicate the read complete
                    if(errno == EAGAIN || errno == EWOULDBLOCK){
                        printf("read later\n");
                        break;
                    }
                    printf("some other error\n");
                    close(sockfd); // will unregister
                    break;
                }else if(ret == 0){
                    printf("client closed.\n");
                    close(sockfd);
                    break;
                }else{
                    printf("get %d bytes from client: %s\n", ret, buf);
                }
            }
        }else{
            // what we are not interested
            printf("something else happened\n");
        }
    }
}

int main (int argc, char *argv[]){
    int sfd, s;
    int efd;
    struct epoll_event event;
    struct epoll_event events[MAXEVENTS];

    if (argc < 2){
        fprintf (stderr, "Usage: %s [port]\n", basename(argv[0]));
        exit (EXIT_FAILURE);
    }

//  const char *ip = argv[1];
//  int port = atoi(argv[2]);

    sfd = create_and_bind (argv[1]);
    if (sfd == -1)
        abort ();

    //listen for connection coming
    s = listen (sfd, SOMAXCONN);
    if (s == -1) {
        perror ("listen");
        abort ();
    }

    //create a epoll object
    efd = epoll_create1 (0);
    if (efd == -1) {
        perror ("epoll_create");
        abort ();
    }

    //add the listen socket to the event poll
    addfd(efd, sfd, 1);

    while(1){
        int ret = epoll_wait(efd, events, MAXEVENTS, -1);
        if(ret < 0){
            printf("epoll wait failture..\n");
            break;
        }
        //et(events, ret, efd, sfd);
        lt(events, ret, efd, sfd);
    }

    close (sfd);

    return EXIT_SUCCESS;
}

高性能网络编程 - epoll机制,布布扣,bubuko.com

时间: 2024-10-28 16:21:47

高性能网络编程 - epoll机制的相关文章

【Unix环境编程】select、poll、epoll机制的联系与区别

在linux设计并发网络程序,主要有如下几种模型:Apache模型(Process Per Connection, PPC).TPC(Thread Per Connection)模型,select机制.poll机制以及epoll. 1.  PPC/TPC模型 这两种模型思想类似,每一个新的链接就用一个线程或者进程处理.PPC使用使用进程.TPC使用线程.缺点是连接多了以后,这么多进程和线程的切换非常大.因此这类模型的能接受的最大连接数不会太高,一般几百个左右. 2.  select机制 PPC/

嵌入式 Linux网络编程(五)——epoll机制

嵌入式 Linux网络编程(五)--epoll机制 一.epoll简介 epoll是在2.6内核中提出的,是select和poll的增强版本.epoll更加灵活,没有描述符限制,使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中. 1.epoll函数 #include <sys/epoll.h> int epoll_create(int size); 创建一个epoll的句柄,size表示监听的文件描述的数量 int epoll_ctl(int epfd,

高性能网络编程技术

高性能网络编程技术 作者:jmz (360电商技术组) 如何使网络服务器能够处理数以万计的客户端连接,这个问题被称为C10K Problem.在很多系统中,网络框架的性能直接决定了系统的整体性能,因此研究解决高性能网络编程框架问题具有十分重要的意义. 1. 网络编程模型 在C10K Problem中,给出了一些常见的解决大量并发连接的方案和模型,在此根据自己理解去除了一些不实际的方案,并做了一些整理. 1.1.PPC/TPC模型 典型的Apache模型(Process Per Connectio

关于高性能网络编程的一些总结与思考

从IO说起 一直以来我很欣赏Google I/O大会,一方面是由于一些新技术与新产品的介绍让人兴奋,更重要的是我太爱这个名字了^_^,我觉得正好符合了我对整个互联网技术的浅薄思考与认识.I/O系统就是整个互联网的关键接口,整个互联网的底层就是I/O系统织成的抽象意义上的net,建立在物理线路之上.而网络编程显然是合理创造与利用I/O的重要工具.而以太网的TCP/UDP则在通常的网络编程中占据了核心的地位.本文主要基于Linux平台,其他如Windows NT以及一些Unix衍生版都提供了对应的解

Socket网络编程--epoll小结

以前使用的用于I/O多路复用为了方便就使用select函数,但select这个函数是有缺陷的.因为它所支持的并发连接数是有限的(一般小于1024),因为用户处理的数组是使用硬编码的.这个最大值为FD_SETSIZE,这是在<sys/select.h>中的一个常量,它说明了最大的描述符数.但是对于大多数应用程序而言,这个数是够用的,而且有可能还是太大的,多数应用程序只使用3~10个描述符.而如今的网络服务器小小的都有几万的连接,虽然可以使用多线程多进程(也就有N*1024个).但是这样处理起来既

高性能网络编程(1)—accept建立连接?(转载,作者:陶辉)

编 写服务器时,许多程序员习惯于使用高层次的组件.中间件(例如OO(面向对象)层层封装过的开源组件),相比于服务器的运行效率而言,他们更关注程序开发 的效率,追求更快的完成项目功能点.希望应用代码完全不关心通讯细节.他们更喜欢在OO世界里,去实现某个接口.实现这个组件预定义的各种模式.设置组件 参数来达到目的.学习复杂的通讯框架.底层细节,在习惯于使用OO语言的程序员眼里是绝对事倍功半的.以上做法无可厚非,但有一定的局限性,本文讲述的网 络编程头前冠以“高性能”,它是指程序员设计编写的服务器需要

【网络】高性能网络编程--下一个10年,是时候考虑C10M并发问题了

转载:http://www.52im.net/thread-568-1-1.html 1.前言 在本系列文章的上篇中我们回顾了过云的10年里,高性能网络编程领域著名的C10K问题及其成功的解决方案(上篇请见:<高性能网络编程(二):上一个10年,著名的C10K并发连接问题>).本文将讨论单机服务器实现C10M(即单机千万并发连接)的可能性及其思路. 截至目前,40gpbs.32-cores.256G RAM的X86服务器在Newegg网站上的报价是几千美元.实际上以这样的硬件配置来看,它完全可

一文读懂高性能网络编程中的I/O模型

1.前言 随着互联网的发展,面对海量用户高并发业务,传统的阻塞式的服务端架构模式已经无能为力.本文(和下篇<高性能网络编程(六):一文读懂高性能网络编程中的线程模型>)旨在为大家提供有用的高性能网络编程的I/O模型概览以及网络服务进程模型的比较,以揭开设计和实现高性能网络架构的神秘面纱. 限于篇幅原因,请将本文与<高性能网络编程(六):一文读懂高性能网络编程中的线程模型>连起来读,这样会让知识更连贯. 学习交流: - 即时通讯开发交流3群:185926912[推荐] - 移动端IM

初解,Scala语言中基于Actor的并发编程的机制,并展示了在Spark中基于Scala语言的Actor而产生的消息驱动框架Akka的使用,

Scala深入浅出实战中级--进阶经典(第66讲:Scala并发编程实战初体验及其在Spark源码中应用解析)内容介绍和视频链接 2015-07-24 DT大数据梦工厂 从明天起,做一个勤奋的人 看视频.下视频,分享视频 DT大数据梦工厂-Scala深入浅出实战中级--进阶经典:第66讲:Scala并发编程实战初体验及其在Spark源码中的应用解析 本期视频通过代码实战详解了Java语言基于加锁的并发编程模型的弊端以及Scala语言中基于Actor的并发编程的机制,并展示了在Spark中基于Sc