当前位置: 首页 > news >正文

C++学习之本地套接字libevent

1.本地套接字介绍

``c
本地套接字是进程间通信的一种实现方式:
    - 管道: 有名管道, 匿名管道   -> 简单, 推荐使用
    - 内存映射区 -> 不阻塞
    - 信号    -> 携带的信息量少, 并且信号优先级太高, 打乱程序的执行顺序 -> 不推荐
    - 本地套接字 -> 基于网络套接字来实现的
        - 程序实现比较复杂
    - 共享内存   -> 项目1中会用, 效率很高, 但是也不阻塞
    - 消息队列    -> 没讲

本地套接字实现方式:
    - 基于tcp实现 -> 推荐
        - 不需要使用IP和端口, 需要使用套接字文件
        - 套接字文件中不存储数据(类似于有名管道文件)
            - 数据在内核的某块内存中存储着, 套接字文件关联着内核中的内存
            - 通过文件描述符就可以对内核的内存进行读写了
    - 基于udp实现
```

2.本地套接字服务器端通信流程

3.本地套接字客户端通信流程

- 结构体

  ```c
  // 头文件:  sys/un.h
  #include <sys/un.h> 
  #define UNIX_PATH_MAX 108
  struct sockaddr_un {
      sa_family_t sun_family;         // AF_UNIX, AF_LOCAL
      char sun_path[UNIX_PATH_MAX];    // 套接字文件的路径(绝对/相对)
  };
  ```

- 通信流程 - 基于tcp

4.本地套接字客户端和服务器代码

- 服务器端
  
    ```c
    // 1. 创建监听的套接字(文件描述符)
    int lfd = socket(AF_UNIX, SOCK_STREAM, 0);
        - 第一个参数使用: AF_UNIX 或者 AF_LOCAL
        - 第二个参数: 基于tcp使用: SOCK_STREAM, 基于udp使用: SOCK_DGRAM
        - 第三个参数: 0
    // 2. 监听的套接字和本地的套接字文件绑定
    //        本地套接字文件不需要程序猿创建, 绑定成功会自动生成
    struct sockaddr_un addr;
    bind(lfd, (struct sockaddr*)&addr, sizeof(addr));
    // 3. 设置监听
    listen();
    // 4. 等待并接受连接
    //         第二个参数传出的是客户端进程绑定的套接字文件的地址
    accept(lfd, (struct sockaddr*)&cliaddr, &len);
    // 5. 通信
    接收数据: read/recv
    发送数据: write/send
        
    // 6. 断开连接, 关闭文件描述符
    close();
    ```

5.本地套接字测试-bug修改

- 客户端
  
    ```c
    // 1. 创建通信的套接字(文件描述符)
    int fd = socket(AF_UNIX, SOCK_STREAM, 0);
        - 第一个参数使用: AF_UNIX 或者 AF_LOCAL
        - 第二个参数: 基于tcp使用: SOCK_STREAM, 基于udp使用: SOCK_DGRAM
        - 第三个参数: 0
    // 2. 通信的套接字和本地的套接字文件绑定 -> 客户端进程也需要一个套接字文件
    //        本地套接字文件不需要程序猿创建, 绑定成功会自动生成
    struct sockaddr_un addr;
    bind(fd, (struct sockaddr*)&addr, sizeof(addr));
    // 3. 连接服务器进程
    struct sockaddr_un seraddr;    // 初始化服务器绑定的套接字文件
    connect(fd, (struct sockaddr*)&seraddr, sizeof(seraddr));

6.进程间通信的场景

  // 4. 通信
    接收数据: read/recv
    发送数据: write/send
        
    // 5. 断开连接, 关闭文件描述符
    close();

7.libevent特点介绍

- 特点 

  > Libevent 是一个用C语言编写的、轻量级的开源高性能事件的框架,主要有以下几个亮点:
  >
  > - 事件驱动( event-driven),高性能;
  >   - 基于回调
  >     - 需要编写函数的处理动作, 调用程序猿不需要管(程序猿不知道调用的时机)
  >     - 当某个具体的事件产生之后(事件的产生是随机的), 这个函数才会被调用
  >     - 我可以将这个事件的处理动作注册给操作系统或者是框架
  >       - 委托框架或者操作系统检测这个事件, 事件被检测到之后, 处理函数被调用
  > - 轻量级,专注于网络;
  > - 源代码相当精炼、易读;
  >   - 现阶段不建议看
  >   - 如果看建议看低版本的代码
  > - 跨平台,支持 Windows、 Linux、 BSD(是Unix的衍生系统) 和 Mac OS;
  > - 支持多种 I/O 多路复用技术, epoll、 poll、 select 和 kqueue 等;
  >   - linux/max: epoll、 poll, select
  >   - windows: select
  >   - BSD: kqueue 
  >   - I/O操作是异步的, 并且是非阻塞
  >     - read/recv 接收数据, 不是数据到达之后马上就接收了
  >     - 委托内核检测 -> 内核检测到了之后 -> 通知程序猿写的程序 -> 开始处理
  >       - 在单线程/进程的IO转接服务器端, 文件描述的处理是线性的
  >         - 不是时时的通信
  > - 支持 I/O,定时器和信号等事件;
  >   - IO: 读事件, 写事件
  >   - 定时器: 超时处理, 强制解除阻塞的
  >   - 信号: linux中的信号, window中没有信号
  >   - 异常事件
  > - 支持注册事件优先级。
  >   - 可以通过提供的函数进行优先级设置
  >   - 默认只有一个优先级, 所有事件默认优先级相同

8.libevent的安装和解决动态库找不到的问题

```shell
  # 下载地址: http://libevent.org/
  # 源码的方式进行安装
  - 将压缩包解压: libevent-2.1.8-stable.tar.gz
  $ tar zxvf libevent-2.1.8-stable.tar.gz
  - 进入到解压目录 - libevent-2.1.8-stable 
  $ cd libevent-2.1.8-stable 
  - 找 README / README.md / INSTALL -> 从里边找安装步骤
  - 如果没找到, 安照默认的安装流程安装即可
  # 标准的源码安装:
  1. 从当前源码目录中找一个叫 configure 可执行文件, 执行该文件
      $ ./configure  # 检测当前系统的安装环境, 检测没问题会生成 makefile 文件
  2. 根据makefile中的规则构建项目
      # 构建之后会得到可执行程序/动态库/静态库
      $ make
  3. 安装
      - 头文件: 从安装目录拷贝到系统目录头文件目录
      - 库文件: 动态库/静态库, 从安装目录拷贝到系统的库目录
      - 可执行程序: 从安装目录拷贝到系统的二进制目录中 /bin , /usr/bin
      $ sudo make install       # 实际就是文件拷贝

9.事件处理框架的创建

 3. 安装
      - 头文件: 从安装目录拷贝到系统目录头文件目录
      - 库文件: 动态库/静态库, 从安装目录拷贝到系统的库目录
      - 可执行程序: 从安装目录拷贝到系统的二进制目录中 /bin , /usr/bin
      $ sudo make install       # 实际就是文件拷贝
      
  # 如何验证这个库装好了, 并且可以使用?
  进入到安装目录的 sample 目录中, 可以编译 hello-world.c , 动态库名叫: libevent.so
  $ gcc hello-world.c -o hello -levent
  

10.启动事件循环event_base_dispatch()函数

# 2. 事件处理框架 - event_base

> 使用 libevent函数之前需要分配一个或者多个 event_base 结构体。每个event_base 结构体持有一个事件集合,可以检测以确定哪个事件是激活的。每个 event_base 都有一种用于检测哪种事件已经就绪的 “方法”,或者说后端。 

- API函数

  > 在 event_base 底层封装了IO多路转接模型, 并且可以对事件进行检测和处理的
  
  ```c
  // 头文件
  #include <event2/event.h>
  // 创建一个事件处理框架
  struct event_base * event_base_new(void);
  // 释放一个事件处理框架
  // 参数: event_base_new() 函数的返回值
  void event_base_free(struct event_base * base);
  
  // 查看底层支持的IO转接模型
  const char** event_get_supported_methods(void);
  // 查看当前实际处理框架使用的IO转接模型
  const char *event_base_get_method(const struct event_base *base);
  ```

11.事件的终止函数

```c
  // 测试程序
  int main()
  {
      // 1. 创建事件处理框架
      struct event_base* base = event_base_new();
      
      // 2. 查看支持什么样的IO转接函
      // 二级指针指向: char * xx[];
      const char** methods = event_get_supported_methods();
      printf("支持的IO转接模型: \n");
      for(int i=0; methods[i] != NULL; ++i)
      {
          printf("    %s\n", methods[i]);
      }
  
      printf("当前event_base使用的IO转接模型: %s\n", event_base_get_method(base));
  
  
      // 释放资源
      event_base_free(base);
  
      return 0;
  }
  ```

12.事件的创建和销毁struct event

> event_base不停的检测委托的检测是实际是不是发生了, 如果发生了, event_base会调用对应的回调函数, 这个回调函数的用户委托检测事件的时候给的.

- 启动事件循环

  ```c
  // 头文件
  #include <event2/event.h>
  // 启动事件处理框架中的事件循环
  // 参数: event_base_new() 的返回值
  /*
      事件检测过程中的特点:
          举例: 检测读事件
              - 如果对方没有发送数据过来, 会一直持续检测, 等待事件被触发
              - 检测到有读事件, 默认情况下只会被处理一次, 事件循环就停止了, 只能接收一次数据
              - 如果要持续的接收数据, 需要额外设置, 给创建的事件指定 EV_PERSIST 属性
  */ 
  // 这个函数被调用, 内部会进行事件检测, 代码阻

13.将创建的事件添加到事件处理框架event_add()

 // 这个函数被调用, 内部会进行事件检测, 代码阻塞在这一行
  // 条件满足, 检测的就停止了, 应用程序就结束了
  int event_base_dispatch(struct event_base* base);
  
  // 相似的操作 -qt
  int main()
  {
      QApplication a;
      a.exec();    // 应用程序对象开始内部的事件循环
      return 0;
  }
  ```

  

14.事件和事件处理框架之间的框架

- 终止事件循环

  ```c
  // 头文件
  #include <event2/event.h>
  
  // 表示一个时间段, tv_sec + tv_usec
  struct timeval {
      long    tv_sec;      // 秒              
      long    tv_usec;    // 微秒        
  };
  
  // 假设事件循环正在继续, 并且在处理一个实际有效的事件, 代用这个函数终止事件循环
  // 事件循环不会马上终止, 在tv指定的时长之后, 事件就别终止了
  int event_base_loopexit(struct event_base * base, const struct timeval * tv);
  参数:
      - base: 事件处理框架
      - tv: 在指定的时间段之后, 退出事件循环
  // 马上终止事件循环
  int event_base_loopbreak(struct event_base * base);
  ```

15.通过event事件写管道代码

# 4. 事件

- 事件的创建和释放

  ```c
  // 头文件
  #include <event2/event.h>
  
  #define EV_TIMEOUT     0x01    // 定时器超时
  #define EV_READ     0x02    // 读, 检测读缓冲区是否有数据
  #define EV_WRITE     0x04    // 写, 检测写缓冲区是否可写
  #define EV_SIGNAL     0x08    // 信号
  #define EV_PERSIST     0x10    // 设置事件被重复检测
  #define EV_ET         0x20    // 边沿模式

16.使用EVENT事件读写管道测试

// 事件的处理函数, 被libevent框架调用, 实参的指定不是程序猿做的
  // 程序猿可以直接使用回调函数的参数 ===> 需要知道参数的意义
  typedef void (*event_callback_fn)(evutil_socket_t fd, short what, void *arg);
  参数:
      - fd: 文件描述符, 是event_new() 第二个参数
      - what: 记录了实际文件描述符触发的事件
      - arg: event_new() 的最后一个参数
  // 创建一个需要检测的事件, struct event 就是创建出的事件
  // evutil_socket_t == int
  // event_new()函数的本质就是对一个文件描述符进行封装
  // 事件被创建之后, 不能被事件处理框架直接检测
  struct event* event_new(struct event_base * base,evutil_socket_t fd,
                            short what,event_callback_fn cb,void * arg);

17.event_add第二个参数超时时长的说明

参数:
      - base: 事件处理框架
      - fd: 一个文件描述符, 比如管道的文件描述符, 套接字通信的文件描述符
      - what: 要检测的事件, 检测第二个参数 fd 的事件
          - EV_READ: 读事件
          - EV_WRITE: 写事件
          - EV_SIGNAL: 信号事件(linux)
          - EV_ET: 设置边沿模式
      - cb: 函数指针对应一个回调函数, 当检测的事件被触发, 这个函数就被调用
      - arg: 作为实参传递给回调函数cb
          
  // 释放事件资源
  void event_free(struct event * event);

18.struct event中的超时处理

- 事件的添加和删除

  ```c
  struct timeval {
      long    tv_sec;    // 秒                
      long    tv_usec;   // 微妙
  };
  // 事件被创建之后, 不能被事件处理框架直接检测, 需要做添加处理
  int  event_add(struct event * ev,const  struct timeval * tv);
  参数:
      - ev: 通过 event_new() 创建得到的事件
    - tv: 超时时长, 如果不用指定为NULL
          - 检测了一个事件, 并且在tv指定的时间段中没有被触发, 这个事件对应的回调函数会被强制调用
          - 如果指定为NULL, 事件不触发, 对应的回调函数就不被调用
  // 将检测的事件从事件处理框架上删除        
  int  event_del(struct event * ev);

19.带缓冲的事件bufferevent

20.创建带缓冲的事件bufferevent_socket_new()

```c
/*
事件和事件处理框架的关系: struct event 和  struct event_base

1. 创建事件处理框架
    struct event_base* base = event_base_new();
2. 创建事件
    struct event* ev = event_new();
3. 添加到事件处理框架中
    event_add()
4. 启动事件循环
    event_base_dispatch();
        - 添加的事件默认只能被检测一次
        - 如果要持续检测
            - 事件处理完毕之后, 再次添加 event_add()
            - 在创建事的时候, 指定为持续检测 EV_PERSIST
5. 释放资源
    event_free();
    event_base_free();
*/

相关文章:

  • C++ :特殊类设计
  • 空值处理操作符
  • 如何深刻理解Reactor和Proactor
  • MySQL学习笔记四
  • 城电科技 | 太阳能花怎么选择?光伏太阳花的应用场景在哪里?
  • 用 HTML、CSS 和 jQuery 打造多页输入框验证功能
  • ES:geoip_databases
  • AWS SNS深度解析:构建高可用、可扩展的云原生消息通信解决方案
  • 基于Java的人脸识别在线考试系统(jsp+springboot+mysql8.x)
  • 在PPT中同时自动播放多个视频的方法
  • AI智慧共治新未来——社会综合治理智慧化系统
  • 关于Spring MVC中传递数组参数的详细说明,包括如何通过逗号分隔的字符串自动转换为数组,以及具体的代码示例和总结表格
  • 十四届蓝桥杯Java省赛 B组(持续更新..)
  • 使用 Vue 快速集成 FullCalendar 日历组件教程
  • SpringBoot整合sa-token,Redis:解决重启项目丢失登录态问题
  • 滑动窗口-最小覆盖字串
  • UI测试(2)
  • 【Spring】小白速通AOP-日志记录Demo
  • 通信协议详解(九):SENT协议 —— 汽车传感器的“摩斯电码大师”
  • 01.win10/win11安装jdk,保姆级详解拆分步骤及命令的意义和报错解决方案
  • 微信优惠群怎么做网站/seo文章生成器
  • 珠海华中建设工程有限公司网站/百度推广代理公司广州
  • 深圳品牌网站设计电话/竞价推广外包
  • 网站下拉菜单设计/商丘网站优化公司
  • 写作网站5秒不写就删除/全网引流推广
  • WordPress会员中心模板/临沂网站seo