AcWing
  • 首页
  • 活动
  • 题库
  • 竞赛
  • 校园
  • 应用
  • 文章
    • 题解
    • 分享
    • 问答
  • 吐槽
  • 登录/注册

Linux 网络编程---基于libevent库C/S模型的TCP通信的实现(bufferevent 事件模型)

作者: 作者的头像   也许 ,  2022-05-04 15:13:52 ,  所有人可见 ,  阅读 37


0


libevent库bufferevent事件

带缓冲区的事件 bufferevent

    #include <event2/bufferevent.h> 

    read/write 两个缓冲. 借助 队列.


创建、销毁bufferevent:

    struct bufferevent *ev;

    struct bufferevent *bufferevent_socket_new(struct event_base *base, evutil_socket_t fd, enum bufferevent_options options);

        base: event_base

        fd: 封装到bufferevent内的 fd

        options:BEV_OPT_CLOSE_ON_FREE :释放 bufferevent 时关闭底层传输端口。这将 关闭底层套接字,释放底层 

        bufferevent

    返回: 成功创建的 bufferevent事件对象。


    void  bufferevent_socket_free(struct bufferevent *ev);


给bufferevent设置回调:

    对比event:    event_new( fd, callback );                      event_add() -- 挂到 event_base 上。


            bufferevent_socket_new(fd)  bufferevent_setcb( callback )

    void bufferevent_setcb(struct bufferevent * bufev,
                bufferevent_data_cb readcb,
                bufferevent_data_cb writecb,
                bufferevent_event_cb eventcb,
                void *cbarg );

    bufev: bufferevent_socket_new() 返回值

    readcb: 设置 bufferevent 读缓冲,对应回调  read_cb{  bufferevent_read() 读数据  }

    writecb: 设置 bufferevent 写缓冲,对应回调 write_cb {  } -- 给调用者,发送写成功通知。  可以 NULL

    eventcb: 设置 事件回调。   也可传NULL

        typedef void (*bufferevent_event_cb)(struct bufferevent *bev,  short events, void *ctx);

        void event_cb(struct bufferevent *bev,  short events, void *ctx)
        {

            。。。。。
        }

        events: BEV_EVENT_CONNECTED


    cbarg:  上述回调函数使用的 参数。

    read 回调函数类型:

        typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void*ctx);

        void read_cb(struct bufferevent *bev, void *cbarg )
        {
            .....
            bufferevent_read();   --- read();
        }

    bufferevent_read()函数的原型:

        size_t bufferevent_read(struct bufferevent *bev, void *buf, size_t bufsize);


    write 回调函数类型:

        int bufferevent_write(struct bufferevent *bufev, const void *data,  size_t size); 


启动、关闭 bufferevent的 缓冲区:

    void bufferevent_enable(struct bufferevent *bufev, short events);   启动  

        events: EV_READ、EV_WRITE、EV_READ|EV_WRITE

        默认、write 缓冲是 enable、read 缓冲是 disable

            bufferevent_enable(evev, EV_READ);      -- 开启读缓冲。


libevent库实现C/S模型通信

客户端流程:

    流程与基于socket套接字的流程一样,但是需要将套接字换成bufferevent事件,事实上,可以将buffevent事件看成

    socket套接字与事件和事件处理器(回调函数)封装在一起的产物。

    1. 创建bufferevent事件 :struct bufferevent *bufferevent_socket_new(struct event_base *base, evutil_socket_t fd, enum bufferevent_options options);

    2. 连接服务器: bufferevent_socket_connect(struct bufferevent *bev, struct sockaddr serv, size_t len);

    3. 给bufferevent事件设置回调函数(注意与event事件的区别,event事件创建时就把回调函数封装进事件了)

        void bufferevent_setcb(struct bufferevent * bufev,
                    bufferevent_data_cb readcb,
                    bufferevent_data_cb writecb,
                    bufferevent_event_cb eventcb,
                    void *cbarg );

    4. 设置读回调生效。默认、write 缓冲是 enable、read 缓冲是 disable(注意!!!)

服务器流程:

    1. 创建event_base

        struct event_base* base = event_base_new();

    2. 使用 evconnlistener_new_bind 创建监听服务器, 
    设置其回调函数,当有客户端成功连接时,这个回调函数listner_cb会被调用。

        ----实现socket();bind();listen();accept()的功能;

            struct evconnlistener * listner

            struct evconnlistener *evconnlistener_new_bind (    
                struct event_base *base,
                evconnlistener_cb cb, 
                void *ptr, 
                unsigned flags,
                int backlog,
                const struct sockaddr *sa,
                int socklen);

            base: event_base

            cb: 回调函数。 一旦被回调,说明在其内部应该与客户端完成(内核调用), 数据读写操作,进行通信。

                typedef void (*evconnlistener_cb)(struct evconnlistener *listener, evutil_socket_t sock, struct sockaddr *addr, int len, void * ptr);

                    listener 参数是接收连接的连接监听器 。 

                    sock 参数是 新接收的套接字。 (用于与客户端通信的cfd, 原来由accept返回值返回)

                    addr 和 len 是接收连接的地址和地址长度。(客户端的地址和地址长度,原来是accept参数返回)

                    ptr 是调 用 evconnlistener_new() 时用户提供的指针。

            ptr: 回调函数的参数

            flags: LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE

                LEV_OPT_REUSEABLE:设置这个标志会让 libevent 标记套接字是可重用的,这样一旦关闭,可 
                以立即打开其他套接字,在相同端口进行监听

            backlog: listen() 2参。 -1 表最大值

            sa:服务器自己的地址结构体

            socklen:服务器自己的地址结构体大小。

            返回值:成功创建的监听器。


    3. 封装 listner_cb() 在函数内部。

        2.1 创建bufferevent事件对象。bufferevent_socket_new(); 

            参数需要与客户端通信的cfd,listner_cb回调后才能得到cfd,所以要在回调函数里创建事件

        2.2 使用bufferevent_setcb() 函数给 bufferevent的 read、write、event 设置回调函数。

        2.3 当监听的 事件满足时,read_cb会被调用, 在其内部 bufferevent_read();读

    4. 设置读缓冲、写缓冲的 使能状态 enable、disable

    5. 启动循环 event_base_dispath();

    6. 释放连接。


client.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <event2/bufferevent.h>
#include <event2/event.h>
#include <arpa/inet.h>

void read_cb(struct bufferevent *bev, void *arg)
{
    char buf[1024] = {0}; 
    bufferevent_read(bev, buf, sizeof(buf));

    printf("fwq say:%s\n", buf);

    bufferevent_write(bev, buf, strlen(buf)+1);
    sleep(1);
}

void write_cb(struct bufferevent *bev, void *arg)
{
    printf("----------我是客户端的写回调函数,没卵用\n"); 
}

void event_cb(struct bufferevent *bev, short events, void *arg)
{
    if (events & BEV_EVENT_EOF)
    {
        printf("connection closed\n");  
    }
    else if(events & BEV_EVENT_ERROR)   
    {
        printf("some other error\n");
    }
    else if(events & BEV_EVENT_CONNECTED)
    {
        printf("已经连接服务器...\\(^o^)/...\n");
        return;
    }

    // 释放资源
    bufferevent_free(bev);
}

// 客户端与用户交互,从终端读取数据写给服务器
void read_terminal(evutil_socket_t fd, short what, void *arg)
{
    // 读数据
    char buf[1024] = {0};
    int len = read(fd, buf, sizeof(buf));

    struct bufferevent* bev = (struct bufferevent*)arg;
    // 发送数据
    bufferevent_write(bev, buf, len+1);
}

int main(int argc, const char* argv[])
{
    struct event_base* base = NULL;
    base = event_base_new();

    int fd = socket(AF_INET, SOCK_STREAM, 0);

    // 通信的fd放到bufferevent中
    struct bufferevent* bev = NULL;
    bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);

    // init server info
    struct sockaddr_in serv;
    memset(&serv, 0, sizeof(serv));
    serv.sin_family = AF_INET;
    serv.sin_port = htons(9876);
    inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr.s_addr);

    // 连接服务器
    bufferevent_socket_connect(bev, (struct sockaddr*)&serv, sizeof(serv));

    // 设置回调
    bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);

    // 设置读回调生效
    // bufferevent_enable(bev, EV_READ);

    // 创建事件
    struct event* ev = event_new(base, STDIN_FILENO, EV_READ | EV_PERSIST,
                                 read_terminal, bev);
    // 添加事件                     
    event_add(ev, NULL);

    event_base_dispatch(base);

    event_free(ev);

    event_base_free(base);

    return 0;
}

server.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <event2/event.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>

// 读缓冲区回调
void read_cb(struct bufferevent *bev, void *arg)
{
    char buf[1024] = {0};   
    bufferevent_read(bev, buf, sizeof(buf));
    printf("client say: %s\n", buf);

    char *p = "我是服务器, 已经成功收到你发送的数据!";
    // 发数据给客户端
    bufferevent_write(bev, p, strlen(p)+1);
    sleep(1);
}

// 写缓冲区回调
void write_cb(struct bufferevent *bev, void *arg)
{
    printf("I'm服务器, 成功写数据给客户端,写缓冲区回调函数被回调...\n"); 
}

// 事件
void event_cb(struct bufferevent *bev, short events, void *arg)
{
    if (events & BEV_EVENT_EOF)
    {
        printf("connection closed\n");  
    }
    else if(events & BEV_EVENT_ERROR)   
    {
        printf("some other error\n");
    }

    bufferevent_free(bev);    
    printf("buffevent 资源已经被释放...\n"); 
}



void cb_listener(
        struct evconnlistener *listener, 
        evutil_socket_t fd, 
        struct sockaddr *addr, 
        int len, void *ptr)
{
   printf("connect new client\n");

   struct event_base* base = (struct event_base*)ptr;
   // 通信操作
   // 添加新事件
   struct bufferevent *bev;
   bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);

   // 给bufferevent缓冲区设置回调
   bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);
   bufferevent_enable(bev, EV_READ);
}


int main(int argc, const char* argv[])
{

    // init server 
    struct sockaddr_in serv;

    memset(&serv, 0, sizeof(serv));
    serv.sin_family = AF_INET;
    serv.sin_port = htons(9876);
    serv.sin_addr.s_addr = htonl(INADDR_ANY);

    struct event_base* base;
    base = event_base_new();
    // 创建套接字
    // 绑定
    // 接收连接请求
    struct evconnlistener* listener;
    listener = evconnlistener_new_bind(base, cb_listener, base, 
                                  LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 
                                  36, (struct sockaddr*)&serv, sizeof(serv));

    event_base_dispatch(base);

    evconnlistener_free(listener);
    event_base_free(base);

    return 0;
}



0 评论

你确定删除吗?
1024
x

© 2018-2022 AcWing 版权所有  |  京ICP备17053197号-1
用户协议  |  常见问题  |  联系我们
AcWing
请输入登录信息
更多登录方式: 微信图标 qq图标
请输入绑定的邮箱地址
请输入注册信息