IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> libevent实现TCP服务器通信 -> 正文阅读

[网络协议]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运行结果

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

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-08-10 13:47:35  更:2021-08-10 13:48:39 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 10:36:26-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码