zoukankan      html  css  js  c++  java
  • 使用libevent实现一个简单的tcp服务端

    一、概述

      1.特点:

         1.事件驱动、高性能、轻量级、专注于网络

         2.源代码精炼、易读

         3.跨平台

         4.支持多种I/O多路复用技术,如epoll 、poll 、select等

         5.支持I/O和信号等事件

      2.使用libevent 函数之前需要分配一个或者多个 event_base 结构体, 每个event_base结构体持有一个事件集合, 可以检测以确定哪个事件是激活的, event_base结构相当于epoll红黑树的树根节点, 每个event_base都有一种用于检测某种事件已经就绪的 “方法”(回调函数)

    通常情况下可以通过event_base_new函数获得event_base结构。

      3.相关函数

    1 struct event_base *event_base_new(void);    
      函数说明: 获得event_base结构
      参数说明: 无
      返回值: 
          成功返回event_base结构体指针;
          失败返回NULL;
    
    2 void event_base_free(struct event_base *);   
        函数说明: 释放event_base指针
    
    3 int event_reinit(struct event_base *base);  
        函数说明: 如果有子进程, 且子进程也要使用base, 则子进程需要对event_base重新初始化, 此时需要调用event_reinit函数.
      函数参数: 由event_base_new返回的执行event_base结构的指针
      返回值: 成功返回0, 失败返回-1
    
    对于不同系统而言, event_base就是调用不同的多路IO接口去判断事件是否已经被激活, 对于linux系统而言, 核心调用的就是epoll, 同时支持poll和select.
    
    查看libevent支持的后端的方法有哪些:
    const char **event_get_supported_methods(void);
    函数说明: 获得当前系统(或者称为平台)支持的方法有哪些
    参数: 无
    返回值: 返回二维数组, 类似与main函数的第二个参数**argv.
    
    const char * event_base_get_method(const struct event_base *base);
    函数说明: 获得当前base节点使用的多路io方法
    函数参数: event_base结构的base指针.
    返回值: 获得当前base节点使用的多路io方法的指针
    
    libevent在地基打好之后, 需要等待事件的产生, 也就是等待事件被激活, 所以程序不能退出, 对于epoll来说, 
    我们需要自己控制循环, 而在libevent中也给我们提供了API接口, 类似while(1)的功能. 
    int event_base_dispatch(struct event_base *base);   //
    函数说明: 进入循环等待事件
    参数说明:由event_base_new函数返回的指向event_base结构的指针
    调用该函数, 相当于没有设置标志位的event_base_loop。程序将会一直运行, 直到没有需要检测的事件了, 或者被结束循环的API终止。
    int event_base_loopexit(struct event_base *base, const struct timeval *tv);
    int event_base_loopbreak(struct event_base *base);
    struct timeval {
      long    tv_sec;                    
      long    tv_usec;            
    };
    
    两个函数的区别是如果正在执行激活事件的回调函数, 那么event_base_loopexit将在事件回调执行结束后终止循环(如果tv时间非NULL, 那么将等待tv设置的时间后立即结束循环), 而event_base_loopbreak会立即终止循环。
    
    typedef void (*event_callback_fn)(evutil_socket_t fd, short events, void *arg);
    struct event *event_new(struct event_base *base, evutil_socket_t fd, short events, event_callback_fn cb, void *arg);
    函数说明: event_new负责创建event结构指针, 同时指定对应的地基base,      还有对应的文件描述符, 事件, 以及回调函数和回调函数的参数。
    参数说明:
    base: 对应的根节点--地基
    fd: 要监听的文件描述符
    events:要监听的事件
          #define  EV_TIMEOUT    0x01   //超时事件
          #define  EV_READ       0x02    //读事件
          #define  EV_WRITE      0x04    //写事件
          #define  EV_SIGNAL     0x08    //信号事件
          #define  EV_PERSIST     0x10    //周期性触发
          #define  EV_ET         0x20    //边缘触发, 如果底层模型支持设置                    则有效, 若不支持则无效.
          
    若要想设置持续的读事件则: EV_READ | EV_PERSIST
    cb 回调函数, 原型如下:
    typedef void (*event_callback_fn)(evutil_socket_t fd, short events, void *arg);
    注意: 回调函数的参数就对应于event_new函数的fd, event和arg
    
    #define evsignal_new(b, x, cb, arg)             \                                                                    
          event_new((b), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg))
    
    int event_add(struct event *ev, const struct timeval *timeout);
    函数说明: 将非未决态事件转为未决态, 相当于调用epoll_ctl函数(EPOLL_CTL_ADD), 开始监听事件是否产生, 相当于epoll的上树操作.
    参数说明:
      ev: 调用event_new创建的事件
    timeout: 限时等待事件的产生, 也可以设置为NULL, 没有限时。
    
    int event_del(struct event *ev);
    函数说明: 将事件从未决态变为非未决态, 相当于epoll的下树(epoll_ctl调用      EPOLL_CTL_DEL操作)操作。
    参数说明: ev指的是由event_new创建的事件.
    
    void event_free(struct event *ev);
    函数说明: 释放由event_new申请的event节点。

    二、示例代码

    //编写libevent服务端
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <unistd.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <event2/event.h>
    
    struct event *connev = NULL;
    void readcb(evutil_socket_t fd,short events,void *arg){
        int n;
        char buf[1024];
        memset(buf,0x00,sizeof(buf));
        n = read(fd,buf,sizeof(buf));
        if(n<=0){
            close(fd);
            //将通讯文件描述符对应的事件从base地基上删除
            event_del(connev);
        }else{
            write(fd,buf,n);
        }
    }
    
    
    //创建连接回调事件
    void conncb(evutil_socket_t fd,short events ,void *arg){
        struct event_base *base = (struct event_base*)arg;
    
        //接收新的客户端连接
        int cfd = accept(fd,NULL,NULL);
        if(cfd>0){
            //创建通信文件描述对应的事件并设置回调函数为readcb
            connev = event_new(base,cfd,EV_READ|EV_PERSIST,readcb,NULL);
            if(connev==NULL){
                //退出循环
                event_base_loopexit(base,NULL);
            }
            //将通信文件描述符对应的事件上event_base地基
            event_add(connev,NULL);
    
        }
    }
    
    
    int main(int argc, char const *argv[])
    {
        
        //1.创建socket
        int lfd = socket(AF_INET,SOCK_STREAM,0);
    
        //2.设置端口复用
        int opt;
        setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
    
        //3.绑定
        struct sockaddr_in serv;
        bzero(&serv,sizeof(serv));
        serv.sin_addr.s_addr = htonl(INADDR_ANY);
        serv.sin_port = htons(8888);
        serv.sin_family = AF_INET;
        bind(lfd,(struct sockaddr *)&serv,sizeof(serv));
    
        //4.监听
        listen(lfd,128);
    
        //5.创建地基
        struct event_base *base = event_base_new();
        if(base==NULL){
            perror("event_base_new error");
            return -1;
        }
    
        //6.创建文件描述符对应的事件
        struct event *ev = event_new(base,lfd,EV_READ|EV_PERSIST,conncb,base);
        if(ev==NULL){
            perror("event_new error");
            return -1;
        }
    
        //7.将新的事件节点上base地基
        event_add(ev,NULL);
        //8.进行事件分发(进入事件循环等待)
        event_base_dispatch(base);
    
        //9.释放资源
    
        event_base_free(base);
        event_free(ev);
    
        close(lfd);
        return 0;
    }
  • 相关阅读:
    Go基础系列:流程控制结构
    Go基础系列:数据类型转换(strconv包)
    Go基础系列:简单数据类型
    Go基础系列:常量和变量
    Go基础系列:map类型
    Go基础系列:Go slice详解
    go基础系列:数组
    Go基础系列:import导包和初始化阶段
    Go基础系列:构建go程序
    go基础系列:结构struct
  • 原文地址:https://www.cnblogs.com/tony-yang-flutter/p/15714118.html
Copyright © 2011-2022 走看看