libevent实现TCP服务器通信


【基于“事件”的异步通信模型】依赖回调机制

官方网站:libevent官方网站
相关资源:libevent相关资源

百度网盘链接:https://pan.baidu.com/s/17LeA3aGxgU7QUUFBt7zfpw
提取码:0ae7

1.libevent库安装

1.1 libevent库优点

  1. 事件驱动,高性能
  2. 开源,精简;
  3. 轻量级,专注于网络;
  4. 跨平台,支持Windows、Linux、Mac Os等;
  5. 支持多种I/O多路复用技术,epoll、poll、dev/poll、select和kqueue等;
  6. 支持I/O,定时器和信号等事件

1.2源码包安装步骤

  1. ./configure 检查安装环境,生成makefile;
  2. make 生成可执行文件;
  3. sudo make install 将必要的资源cp置系统指定目录
  4. 进入 sample 目录,运行demo验证库安装使用情况。
  5. 编译使用库时,需要加 -levent 选项。
    请添加图片描述
    请添加图片描述
      运行ln -s /usr/local/lib/libevent-1.4.so.2 /usr/lib/libevent-1.4.so.2命令(这是为了防止在系统默认路径下 找不到库文件,也可以使用gcc中的-L参数来指定库文件的位置所在)

2.libevent框架

  1. 创建event_base
struct event_base *event_base_new(void);

struct event_base *base =event_base_new();
  1. 创建事件
  //常规事件event
event_new();
  //带缓冲区的事件bufferevent  
bufferevent_socket_new();
  1. 将事件添加到 base上
int event_add(struct event *ev, const struct timeval *tv)
  1. 循环监听事件满足
int event_base_dispatch(struct event_base *base);
event_base_dispatch(base);
  1. 释放evemt_base
event_base_free(base);

2.1创建事件

struct event *ev;
struct event *event_new(struct event_base *base,evutil_socket_t fd,short what,event_callback_fn cb;  void *arg);

参数:

  base: event_base_new()返回值

  fd: 绑定到 event 上的文件描述符

  what:对应的事件(r、w、e)

    EV_READ 一次读事件

    EV_WRTIE 一次写事件

    EV_PERSIST 持续触发。 结合 event_base_dispatch 函数使用,生效。

  cb:一旦事件满足监听条件,回调的函数

  typedef void (*event_callback_fn)(evutil_socket_t fd, short, void *)

  arg: 回调的函数的参数

返回值:成功创建的 event

2.2添加事件到 event_base

int event_add(struct event *ev, const struct timeval *tv);

参数:

  ev: event_new() 的返回值。

  tv:NULL(不会超时)

2.3释放事件

int event_free(struct event *ev);

  ev: event_new() 的返回值。

从event_base上删除事件【了解】

int event_del(struct event *ev);

  ev: event_new() 的返回值。

3.未决和非未决

未决事件:有资格被处理,但尚未被处理

非未决事件:没有资格被处理
请添加图片描述)

4.带缓冲区的Bufferevent

4.1Bufferevent结构体

#include <event2/bufferevent.h> 

bufferevent有两个缓冲区,队列实现,数据只能读一次

  读缓冲:读取了任意量的数据之后会调用读回调函数,使用bufferevent_read()函数从读缓冲区读取数据。

  写缓冲:bufferevent_write()函数向写缓冲中写入数据,写缓冲一旦有数据就会自动刷新,将数据发送给对端,写完后调用写回调函数。

4.2Bufferevent事件创建、释放

4.2.1创建基于套接字的 bufferevent

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

参数:

  base:event _base

  fd:封装到bufferevent的文件描述符

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

返回:

​  ​成功:函数返回一个 bufferevent

  ​ ​失败:返回 NULL

4.2.2. 释放bufferevent操作

void bufferevent_free(struct bufferevent *bev);

4.3给读写缓冲区设置回调

bufferevent_setcb()函数修改 bufferevent 的一个或者多个回调

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

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

  1. readcb对应回调函数【从bufferevent的输入缓冲区移除数据】

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

    读数据:从bufferevent输入缓冲区中移除数据(队列)

    size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
    
  2. writecb对应回调函数【向bufferevent的输出缓冲区添加数据】

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

    bufferevent_write()将内存中从 data 处开 始的 size 字节数据添加到输出缓冲区的末尾

    int bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf);
    

    bufferevent_write_buffer()移除 buf 的所有内 容,将其放置到输出缓冲区的末尾

  3. eventcb对应回调函数

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

​  ​BEV_EVENT_READING:读取操作时发生某事件,具体是哪种事件请看其他标志。

​  ​BEV_EVENT_WRITING:写入操作时发生某事件,具体是哪种事件请看其他标志。

  ​BEV_EVENT_ERROR : 操 作 时 发 生 错 误 。 关 于 错 误 的 更 多 信 息 , 请 调 用

​  ​EVUTIL_SOCKET_ERROR()。

​  ​BEV_EVENT_TIMEOUT:发生超时.

​  ​BEV_EVENT_EOF:遇到文件结束指示。

​  ​BEV_EVENT_CONNECTED:请求的连接过程已经完成。

4.4禁用、启用缓冲区

void bufferevent_enable(struct bufferevent *bufev, short events) ;//启用
void bufferevent_disable(struct bufferevent *bufev, short events) ;//禁用
short bufferevent_get_enabled(struct bufferevent *bufev);//获取缓冲区禁用状态

events:EV_READ、EV_WRITE 或者 EV_READ | EV_WRITE

默认:write 缓冲是 enable、read 缓冲是 disable,调用函数开启读缓冲

4.5客户端连接服务器

客户端:

int bufferevent_socket_connect(struct bufferevent *bev, struct sockaddr *address, int addrlen);

bev:bufferevent事件对象(封装了fd)

address 和 addrlen 参数跟标准调用 connect()的参数相同

【注】:如果使用 bufferevent_socket_connect() 发起连接,将只会收到 BEV_EVENT_CONNECTED 事件。如果自己调用 connect(),则连接上将被报告为写入事件。

4.6服务器创建监听器

#include <event2/listener.h> 
evconnlistener_new*()函数分配和返回一个新的连接监听器对象
struct evconnlistener * evconnlistener_new(
    struct event_base *base
    evconnlistener_cb cb, 
    void *ptr, 
    unsigned flags,
    int backlog , 
    evutil_socket_t fd);
evconnlistener_new_bind(
    struct event_base *base,
    evconnlistener_cb cb,
    void *ptr,
    unsigned flags,
    int backlog , 
    const struct sockaddr *sa, 
    int socklen);

参数:

  ​cb:监听回调函数,一旦回调,内部与客户端连接完成,进行数据通信

  ​ptr:传递给回调函数

  ​flags:“可识别的标志”

  ​  ​LEV_OPT_CLOSE_ON_FREE :如果设置了这个选项,释放连接监听器会关闭底层套接字

  ​  ​LEV_OPT_REUSEABLE :端口复用

  ​backlog:任何 时刻网络栈允许处于还未接受状态的最大未决连接数。-1:表示使用默认最大值

  ​sa:服务器的地址结构

  ​socklen:地址结构大小

返回:成功创建的监听器

释放监听器

void evconnlistener_free(struct evconnlistener *lev);

回调函数类型

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

参数:

​ listener:接收连接的连接监听器

​ sock:用于通信的文件描述符

​ addr:客户端地址结构

​ len:客户端地址结构的长度

​ ptr:调用 evconnlistener_new() 时用户提供的指针

【注】:该回调函数由框架自动调用

5.libevent实现TCP服务器通信

5.1服务端实现TCP连接

  1. 创建event_base

  2. 创建bufferevent事件对象,调用bufferevent_socket_new()函数

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

  4. 当监听的事件满足时,read_cb会被调用,在其内部 bufferevent_read()读取数据

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

  6. 在函数内部封装 listner_cb() 函数。完成与客户端通信

  7. 启动循环event_base_dispatch()

  8. 设置读写缓冲的使能状态

  9. 释放连接

5.2客户端实现TCP连接

  1. 创建event_base

  2. 创建一个用于跟服务器通信的bufferevent事件对象,调用bufferevent_socket_new()函数

  3. 与服务器建立连接,调用bufferevent_socket_connect()函数

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

  5. 设置读写缓冲的使能状态

  6. 接收、发送数据,bufferevent_read()、bufferevent_write()

  7. 启动循环监听event_base_dispatch()

  8. 释放连接

5.3服务端程序

#include <iostream>
#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>

using namespace std;
//读缓冲区回调
void read_cb(struct bufferevent *bev, void *data)
{
    char buf[1024]={0};
    //读取数据
    bufferevent_read(bev,buf,sizeof(buf));
    cout<<"client:"<<buf<<endl;

    char *p="I am server,I have received your data";
    //写数据给客户端  给bufferevent写缓冲写数据,写缓冲将数据刷给对端,完成后回调函数
    bufferevent_write(bev,p,strlen(p)+1);
    sleep(1);
}
//写缓冲区回调
void write_cb(struct bufferevent *bev, void *data)
{
    cout<<"成功将数据写给客户端"<<endl;
}
//事件回调
void event_cb(struct bufferevent *bev, short events, void *data)
{
    if (events & BEV_EVENT_EOF)
    {
        cout<<"connection closed"<<endl;  
    }
    else if(events & BEV_EVENT_ERROR)   
    {
        cout<<"some other error"<<endl;
    }
    
    bufferevent_free(bev);    
    cout<<"buffevent 资源已经被释放"<<endl; 
}
//监听回调函数
void cb_listener(
    struct evconnlistener *listener, 
    evutil_socket_t fd,
    struct sockaddr *addr, 
    int len, void *ptr)
{
    cout<<"connect new client"<<endl;
    struct event_base*base=(struct event_base*)ptr;
    //创建bufferevent事件对象
    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);
    return ;
}

int main (int argc,const char*argv[])
{
    //创建地址结构并初始化
    struct sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(9876);
    server.sin_addr.s_addr = htonl(INADDR_ANY);

    //创建event_base
    struct event_base *base;
    base=event_base_new();

    //创建监听器(创建套接字,绑定,接收收连接请求)
    struct evconnlistener *listener;
    //evconnlistener_new_bind()函数返回监听器对象
    listener=evconnlistener_new_bind(
            base,cb_listener,base,
            LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
            -1, 
            (struct sockaddr *)&server, 
            sizeof(server)); 

    //启动循环监听
    event_base_dispatch(base);
    //释放监听器
    evconnlistener_free(listener);
    //释放event_base
    event_base_free(base);
    return 0;
}

5.4客户端程序

#include <iostream>
#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>

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

    cout<<"fwq say:"<<buf<<endl;
    bufferevent_write(bev, buf, strlen(buf)+1);
    sleep(1);
}

void write_cb(struct bufferevent *bev, void *arg)
{
    cout<<"----------我是客户端的写回调函数"<<endl; 
}

void event_cb(struct bufferevent *bev, short events, void *arg)
{
    if (events & BEV_EVENT_EOF)
    {
        cout<<"connection closed"<<endl;  
    }
    else if(events & BEV_EVENT_ERROR)   
    {
       cout<<"some other error"<<endl;
    }
    else if(events & BEV_EVENT_CONNECTED)
    {
        cout<<"已经连接服务器..."<<endl;
        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;
}

5.5运行结果

服务端:
请添加图片描述)
客户端:
请添加图片描述)

  • 4
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
作为一名AI,我并不懂得如何使用libevent,但是我可以为你提供一些libevent的基础知识和参考资料。 libevent是一个开源的事件驱动库,它提供了一种跨平台的方式来处理网络事件。它可以用于编写高性能的网络应用程序,如Web服务器、代理服务器、消息队列等。libevent支持多种网络协议,包括TCP、UDP、HTTP等。 要使用libevent实现WebSocket服务器和客户端,你需要先了解WebSocket协议的基础知识。WebSocket是一种基于TCP的全双工协议,它允许客户端和服务器之间进行双向通信。WebSocket协议的核心是建立一个长时间的TCP连接,然后通过发送HTTP请求和响应来升级到WebSocket连接。一旦WebSocket连接建立,客户端和服务器可以通过发送消息进行通信。 在使用libevent实现WebSocket服务器和客户端时,你需要使用libevent提供的事件循环机制来处理网络事件。你可以创建一个事件循环,并注册事件回调函数来处理不同类型的网络事件,如TCP连接、HTTP请求、WebSocket消息等。在处理WebSocket消息时,你需要按照WebSocket协议的规范解析消息,并根据消息类型进行相应的处理。 下面是一些参考资料,可以帮助你更好地了解libevent和WebSocket协议: 1. libevent官方网站:http://libevent.org/ 2. WebSocket协议规范:https://tools.ietf.org/html/rfc6455 3. libevent实现WebSocket服务器的示例代码:https://github.com/libevent/libevent/blob/master/sample/websocket-server.c 4. libevent实现WebSocket客户端的示例代码:https://github.com/libevent/libevent/blob/master/sample/websocket-client.c 希望这些资料可以帮助你更好地理解使用libevent实现WebSocket服务器和客户端的基本原理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值