libevent框架、带缓冲区事件bufferevent的使用

1.简介

特点

源码包安装

2.libevent框架

创建event_base

创建添加事件

循环监听事件满足

释放event_base

相关函数了解

3.常规事件event

未决与非未决

使用fifo的读写

4.带缓冲区事件bufferevent

bufferevent

A.服务器创建监听器

C.给读写缓冲区设置回调

D.禁用、启用缓冲区

E.客户端连接服务器

server

client


1.简介

libevent

libevent版本一共有1.4系列和2.0系列两个稳定版本。

1.4系列比较古老,但是源码简单,适合源码的学习

2.0系列比较新,建议直接使用2.0

需要注意的是,1.4系列和2.0系列两个版本的接口并不兼容,就是2.0将一些接口的原型发>生了改变,所以将1.4升级到2.0需要重新编码。

特点

开源、精简、跨平台、专注于网络通信

基于“事件”异步通信模型 -- 回调

事件:event库中将所有的事情封装成了事件,就像操作系统将所有的东西成文件

异步:对应同步(所有的事情都要讲究先后顺序),注册函数的时间和函数被调用的时间不同,只有当内核监听到事件的条件满足时,才会去调用函数(回调函数)。和信号一样,先注册信号捕捉函数,有信号来时才会去调用回调函数

核心其实就是epoll

源码包安装

参考README、readme

1.下载压缩包  -->    libevent-2.0.20-stable.tar.gz
2.解压 -->           tar -xvf libevent-2.0.20-stable.tar.gz  
3.进入解压后的目录 -->cd /....
4.检查安装环境,生产makefile -->./configure参数可加可不加
5.生成 .o 和可执行文件 -->make
6.将必要的资源cp到系统指定目录(软、硬连接,必要的.so文件等)sudo make install
7.进入simple目录,运行demo验证库按安装使用情况,要添加上-levent,这里其实是采用动态库链接的方式,去掉lib前缀和.so后缀cd ./simplegcc hello-world.c -o hell -levent//-l库名:指定动态库库名,-L和-I不添加采用默认./a.out

由于编译运行时指定的是动态库,运行可执行文件时动态链接器会去系统指定位置找该动态库,6 中其实就是将软、硬连接,必要的.so文件放到系统固定位置,这样链接时动态链接器才找得到 库.so(是个软链接,然后根据软链接去找到相应的可执行文件的目录)

链接时必要的.so文件存储在/user/lib或是/user/loal/lib

动态链接器查找.so文件的目录一般是/user/lib

2.libevent框架

创建event_base

【头文件】:#include<event2/event.h>
struct event_base *event_base_new(void);
struct event_base *base = NULL;
base = event_base_new();

创建添加事件

1.创建事件event -- 常规事件、带缓冲区事件:strcut event *event_new(strcut event_base *base, evutil_socket_t fd,short what, event_callback_fn cd,void *arg);what: EV_READ、EV_WRITE、EV_PERSIST(连续触发 -- whike(read() / write() ))cd: 回调函数 -- typedef void(*event_callback_fn)(evutil_socket_t fd, short,void *);struct bufferevent *bufferevent_socket_new(struct event_base *base,evutil socket_t fd, enum bufferevent_options options);.base: 函数返回值options:BEV_OPT_CLOSE_FREE2.将事件event添加到base上int event_add(struct event *ev, const struct timeval *tv);ev: event_new()函数返回的事件。tv: 为 NULL,不会超时。意为:一直等到事件被触发,回调函数会被调用。为非 0,等待期间,检查事件没有被触发,时间到,回调函数依旧会被调用。bufferevent暂时不看

循环监听事件满足

启动循环:相当于epoll中while{ epoll_wait(); ... } -- 阻塞监听到客户端的连接请求、读请求等然后调用回调函数进行处理它内部是等待事件满足然后根据事件去调用自己写的对应的回调函数int event_base_dispatch(struct event_base *base);base: event_base_new 函数的返回值返回值:1表示成功,0表示失败只有 event_new 中指定了EV_PERSIST 才持续触发,否则值触发一次,就跳出循环通常这样:EV_WRITEIEV_PERSIST、EV_READIEV_PERSIST在指定事件后停止循环:int event_base_loopexit(struct event_base *base, const struct timeval *tv)
立即停止循环:int evnet_base_loopbread(struct event_base *base);

释放event_base

void event_base_new(struct event_base *base);

相关函数了解

查看 支持哪些 多路 IOconst char **event_get_supported_methods(void);查看 当前用的 多路 IOconst char * event_base_get_method(const struct event base *base);查看 fork后子进程使用的 event_baseint event _reinit(struct event_base *base);成功:0,失败:-1使用该函数后,父进程的base才能在子进程中生效

3.常规事件event

可以用在本地中,如管道

步骤:

1.创建一个事件对象,监听的事件发生等于该ev发生
strcut event *event_new(strcut event_base *base,  //指明事件要插入的底座 -- 要调用event_add才真的插入evutil_socket_t fd,  //事件对象中的fd,将其捆绑事件对象ev上,-- 事件/请求来源,回调函数处理的就是fd的请求short what,          //要监听的事件 -- r、w、e -- r发生,监听条件满足,调用回调读取数据然后处理event_callback_fn cd,//事件发生,监听条件满足,调用该回调函数void *arg);          //回调函数的参数,传base自己或NULLwhat: EV_READ  一次读,event_base_dispatch(base)客户端有数据过来,服务器要读,监听到条件满足,读取然后调用回调函数处理数据,退出结束EV_WRITE 一次写EV_PERSIST 结合event_base_dispatch(base)处理完客户端的请求进入下一轮循环,继续等待客户端的数据然后读取,该函数不退出(连续触发 -- whike(read() / write() ))    cb: 回调函数 -- typedef void(*event_callback_fn)(evutil_socket_t fd, short,void *);返回值:成功则返回创建的事件event2.将事件添到base上,该base在ev当中已经被指明int event_add(struct event *ev, const struct timeval *tv);ev: event_new()函数返回的事件。tv: 为 NULL,不会超时。意为:一直等到事件被触发,回调函数会被调用。为非 0,等待期间,检查事件没有被触发,时间到,回调函数依旧会被调用。一旦事件ev中的对端fd有事件(r、w、e)发生满足监听条件,base监听到将调用ev中的回调函数去处理事件3.将事件从base上拿下
int event_del(struct event *ev);ev: event_new()函数返回的事件4.释放事件
int event_free(struct event *ev);成功:0;失败:-1

未决与非未决

未决:有资格被处理,但还没有被处理(事件还没发生,监听调价按不满足,无法调用回调处理事件 -- 处理数据)

非未决:没有资格被处理(还没准备好,即使事件到来也不会调用回调函数处理事件)

使用fifo的读写

服务器这边:处理读事件 -- 读取出来然后处理数据 -- base在这边

客户端:写事件,写给服务器

4.带缓冲区事件bufferevent

bufferevent

主要应用于套接字通信

#include <event2/bufferevent.h>
原理:bufferevent有两个缓冲区:也是队列实现,读走没,先进先出。读:有数据-->读回调函数被调用-->使用 bufferevent_read() --> 读数据。写:使用 bufferevent_write()-->向写缓冲中写数据 -->该缓冲区有数据自动写出-->写完,回调函数被调用(鸡肋)

A.服务器创建监听器

【头文件】: #include <event2/listener.h>

struct evconnlistener* evconnlistener_new(  struct event base *base,evconnlistener_cb cb,void *ptr,unsigned flags,int backlog,*evutil_socket_t fd
);         【了解】1.【掌握】                                                                
struct evconnlistener *evconnlistener_new_bind (struct event_base *base,   //event_base_new(), 底座的创建evconnlistener_cb cb, //监听器的回调,在里面封装事件创建B、设置缓冲区回调C、启动缓冲区函数Dvoid *ptr,  //cb的参数,cb中添加事件、bufferevent回调函数、设置缓冲区回调等函数都需要base,靠ptr传进回调cb中的回调使用unsigned flags,int backlog,const struct sockaddr *sa,int sockln);cb:监听回调函数。接受连接之后,用户要做的操作ptr:回调函数的参数flags:“可识别的标志”LEV_OPT_CLOSE_ON_FREE:释放 bufferevent 时关闭底层传输端口。这将关闭底层套接字.释放底层 bufferevent等LEV_OPT_REUSEABLE 端口复用。可以“ | ”backlog:listen的2参。传-1:表使用默认最大值sa:服务器IP+Portsocklen:sa的大小
这一个函数,可完成系统调用socket(), bind(), listen(), accept()的作用2. evconnlistener_cb cb回调函数 -- 在该回调函数体内创建事件处理服务器读请求,将B、C、D都封装进该回调
【注:该回调函数不由我们调用,是框架自动调用,因此只需知晓参数含义即可】
typedef void (*evconnlistener_cb) (struct evconnlistener *listener,evutil_socket_t sock,struct sockaddr *addr,int len,void *ptr);listener: evconnlistener_new_bind 函数的返回值sock: 用于通信的文件描述符addr: 客户端的 IP+端口len: addr 的len.ptr:evconnlistener_new_bind()中ptr传递进来的值,一般传base,因为事件创建B、设置缓冲回调C、启动缓冲区函数D参数都需要base

B.带缓冲区的事件对象创建、释放

创建: -- 有客户端连接了才能创建,所以得先创建监听器,在监听器的回调函数中去等待客户端的请求连接然后创建事件绑定,处理请求
struct bufferevent *bufferevent_socket_new(struct event_base *base, evutil_socket_t fd, enum bufferevent_options options);base:event_base_new 函数的返回值。fd:跟 bufferevent绑定的套接字的文件描述符。类比 event_new()options:BEV_OPT_CLOSE_ON_FREE 只用这一个即可。[可参考《libevenet 参考手册(中文版)》p53,“bufferevent 的选项标志”】释放:
void bufferevent_free(struct bufferevent *bev);bev:上面函数的返回值。

C.给读写缓冲区设置回调

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()读,而不是 read()Writecb:不用它,传NULL即可!!!eventcb:可传 NULL,用来处理异常或额外事件的回调cbarg :传给回调函数用的参数。1.bufferevent_data_cb回调函数:
typedef vdid (*bufferevent_data_cb)(struct bufferevent *bev, void*ctx);
如:void read_cb(struct bufferevent *bev, void *arg){ ......//size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);//    通常用在 readcb 中,替代read()!  //库提供的bufferevent_read()//读数据,类似 read()的作用//buffervent_write();//int bufferevent_write(struct bufferevent *bufev, const void *data, size_t size);//    常用在 bufferevent_read 之后,替代 write()!  //库提供的......}  //自己设计的回调函数读数据:从 bufferevnet 输入缓冲区 中 移除数据2.bufferevent_event_cb eventcb回调函数:
void event_cb(struct bufferevent *bev, short events, void *ctx){......buffervent_event_cb(bev,events,ctx);/*typedef void (*bufferevent_event_cb)(struct bufferevent *bev, short events, void *ctx);events:不同标志位,代表不同的事件。【可参考《libevenet 参考手册(中文版)》p52,“回调和水位”】EV_EVENT_READING:读取操作时发生某事件,具体是哪种事件,看其他标志。BEV_EVENT_WRITING:写入操作时发生某事件,具体是哪种事件请看其他标志。BEV_EVENT_ERROR:操作时发生错误。关于错误的更多信息,请调用EVUTILSOCKET_ERRORO()。BEV_EVENT_TIMEOUT:发生超时。BEV_EVENT_EOF:遇到文件结束指示。BEV_EVENT_CONNECTED:请求的连接过程已经完成,实现客户端时可用。/*......
}

D.禁用、启用缓冲区

默认:新建的 bufferevent 写缓冲是enable的。而,读缓冲是 disable 的。
void bufferevent_enable(struct bufferevent *bufev,short events);启用缓冲区events :EV_READ、EV_WRITE、EV_READIEV_WRITE通常用来启用 bufferevent的read 缓冲。void bufferevent_disable(struct bufferevent *bufev,short events); 禁用events :EV_READ、EV_WRITE、EV_READIEV_WRITEshort bufferevent_get_enabled(struct bufferevent *bufev);、获取缓冲区的禁用状态,需要借助 & 来得到。

E.客户端连接服务器

int bufferevent_socket_connect(struct bufferevent *bev, struct sockaddr *address,int addrlen);address、len:类似 connect()中的用法 -- 服务器的地址结构和地址结构的长度bev: 封装了客户端cfd的事件对象

server

#include <stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include<arpa/inet.h>
#include<event2/bufferevent.h>
#include<event2/event.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 的读缓冲。默认是 disable 的bufferevent_enable(bev, EV_READ);
}int main(int argc, const char* argv[]) {// init serverstruct 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();  // 创建事件基础结构//创建套接字//绑定//接收连接请求//事件的创建都应该在cb_listener中创建,先有监听器libevent内部才能去处理请求事件和将新来的事件对象添加给base,不能在main中创建,无法向监听器中传事件对象//不像event可以用add将事件添加给base,启动循环处理请求 -- 还是有差别的,fifo是PIC机制,不是网络通信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));//这一个函数,可完成系统调用socket(), listen(), bind(), accept()的作用,accept返回客户端的套接字,就相当于事件对象的创建//这和在listen创建监听器后才能accept获得客户端套接字一个道理,得先有监听器才能处理链接请求,创建客户端对应的事件对象,监听器内部再将新来的事件对象添加到base上//还有监听器listener监听已经在base上的事件的请求事件//cb_listener函数内核自己调用,监听到事件就会调用,不像epoll自己设定的读请求等回调函数还是需要自己调用的// 启动循环监听event_base_dispatch(base);evconnlistener_free(listener);event_base_free(base);return 0;
}

client

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <arpa/inet.h>
#include <event2/bufferevent.h>
#include <event2/event.h>// 读缓冲区回调
void read_cb(struct bufferevent *bev, void *arg) {char buf[1024] = {0};bufferevent_read(bev, buf, sizeof(buf));printf("server say: %s\n", buf);char *p = "客户端收到服务器数据!";bufferevent_write(bev, p, strlen(p) + 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("已经连接服务器.....\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);// 初始化 server 信息struct sockaddr_in serv; // server 的地址结构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 的读回调bufferevent_enable(bev, EV_READ);// 创建事件struct event *ev = event_new(base, STDIN_FILENO, EV_READ | EV_PERSIST, read_terminal, bev);// 将事件 ev 添加到指定的 base 中event_add(ev, NULL);event_base_dispatch(base); // 循环监听调用回调事件对象 ev 中的 read_terminal 处理请求event_free(ev);event_base_free(base);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/439335.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于spring boot的篮球论坛系统

作者&#xff1a;计算机搬砖家 开发技术&#xff1a;SpringBoot、php、Python、小程序、SSM、Vue、MySQL、JSP、ElementUI等&#xff0c;“文末源码”。 专栏推荐&#xff1a;SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;Java精选实战项…

【Unity踩坑】使用内购时获取Google Play license key

在Unity中使用了IAP&#xff08;内购&#xff09;后&#xff0c;需要设置Google Play license key。 这个key需要在Google Play Console中&#xff08;https://play.google.com/console&#xff09;&#xff0c;找到相应的应用&#xff0c;在左侧“创收设置”里可以找到license…

电脑失声,一招搞定

早已习惯了Edge浏览器的“大声朗读”功能&#xff0c;今天值班&#xff0c;值班室用的两台电脑只配有耳机&#xff0c;没有音箱&#xff0c;顿时感觉不适。 先找了一个带功放的老音箱&#xff0c;发现少了电箱到功放的音频线。 一顿搜索&#xff0c;在找到音频线的同时&#…

Linux·进程概念(下)

1. 进程优先级 优先级就是获得某种资源的先后顺序&#xff0c;因为CPU资源是有限的&#xff0c;因此各个进程之间要去争取CPU的资源。 那么针对Linux操作系统下的PCB中&#xff0c;也就是task_struct结构体中&#xff0c;使用了int类型的变量记录了每个进程的优先级属性&#x…

【Xcode Command Line Tools】安装指南

安装指令 xcode-select --install安装 完成安装 验证 $ xcode-select -p /Library/Developer/CommandLineTools

Python机器视觉:01- 利用列表和切片操作 - 做一个弧线和图片相交的mask区域

前言&#xff1a; Python的列表处理&#xff0c;在机器视觉中经常被用到&#xff0c;这里结合基本的概念机器视觉实践案例&#xff0c;成文如下&#xff1a; 本身将实现一个&#xff0c;弧线的mask填充&#xff1a;这个mask是我的一个天文项目的应用&#xff0c;目的在于将月…

(笔记)第三期书生·浦语大模型实战营(十一卷王场)–书生基础岛第2关---8G 显存玩转书生大模型 Demo

学员闯关手册&#xff1a;https://aicarrier.feishu.cn/wiki/ZcgkwqteZi9s4ZkYr0Gcayg1n1g?open_in_browsertrue 课程视频&#xff1a;https://www.bilibili.com/video/BV18x4y147SU/ 课程文档&#xff1a; https://github.com/InternLM/Tutorial/blob/camp3/docs/L1/Demo/rea…

基于Zynq SDIO WiFi移植三(支持2.4/5G)

应用问题-WIFI作为AP-hostapd多次连接 设备作为WIFI热点时&#xff0c;连接出现了下述问题&#xff1a; 1 手机连接需要三次&#xff0c;三次都需要输入密码&#xff1b; 2 平板连接需要三次&#xff0c;三次都需要输入密码&#xff1b; 3 电脑连接需要一次&#xff0c;无感…

24个AI写作秘技,助你写出震撼人心的佳作!

最近&#xff0c;许多朋友开始尝试使用AI进行写作。然而&#xff0c;他们创作的文章常常显得语言过于正式&#xff0c;不仅缺乏沉浸感&#xff0c;发送后也很容易被系统检测出重复内容。我一直强调&#xff0c;在写作过程中&#xff0c;我们不应完全依赖AI。AI只是一种工具&…

[ComfyUI]Flux:超美3D微观山水禅意,经典中文元素AI重现,佛陀楼阁山水画卷

在数字艺术和创意领域&#xff0c;[ComfyUI]Flux以其独特的虚实结合技术&#xff0c;已经成为艺术家和设计师们手中的利器。今天&#xff0c;我们激动地宣布&#xff0c;[ComfyUI]Flux带来了一款超美的3D微观山水禅意作品&#xff0c;经典中文元素通过AI技术重现&#xff0c;包…

RabbitMQ(死信队列)

一、本文抒写背景 前面我也在延迟队列篇章提到过死信队列&#xff0c;也提到过一些应用场景&#xff01; 今天呢&#xff0c;这篇文章&#xff0c;主要就是实战一个业务场景的小Demo流程&#xff0c;哈哈&#xff0c;那就是延迟关闭订单。 二、开始啦&#xff01;letgo! 首…

No.0 笔记 | 从小白到入门:我的渗透测试笔记

嘿&#xff0c;小伙伴们&#xff01;好久不见啊&#xff0c;是不是都以为我失踪了&#xff1f;&#x1f602; 其实呢&#xff0c;最近一直在埋头苦学&#xff0c;感觉自己就像是在技术的海洋里游泳&#xff0c;每天都在吸收新知识。现在终于有时间冒个泡&#xff0c;跟大家分享…

小红书制作视频如何去原视频音乐,视频如何去原声保留背景音乐?

在视频编辑、音乐制作或个人娱乐中&#xff0c;有时我们希望去掉视频中的原声&#xff08;如对话、解说等&#xff09;&#xff0c;仅保留背景音乐。这种处理能让观众更加聚焦于视频的氛围或节奏&#xff0c;同时也为创作者提供了更多创意空间。选择恰当的背景音乐&#xff0c;…

【记录】Excel|Excel 打印成 PDF 页数太多怎么办

【记录】Excel&#xff5c;解决 Excel 打印成 PDF 页数过多的问题 文章目录 【记录】Excel&#xff5c;解决 Excel 打印成 PDF 页数过多的问题方法一&#xff1a;调整页边距WPS OfficeMicrosoft Excel 方法二&#xff1a;优化页面布局调整列宽和行高使用“页面布局”视图合并单…

小程序-使用npm包

目录 Vant Weapp 安装 Vant 组件库 使用 Vant 组件 定制全局主题样式 API Promise化 1. 基于回调函数的异步 API 的缺点 2. 什么是 API Promise 化 3. 实现 API Promise 化 4.调用 Promise 化之后的异步 API 小程序对 npm 的支持与限制 目前&#xff0c;小程序中已经…

BUSHOUND的抓包使用详解

BUSHOUND是个过滤软件&#xff0c;确切来说是在windows操作系统它的驱动层USB传输的数据。所以这个数据上可能是与USB的总线上的数据是有一点差异的。 先要选择设备的抓包。所以就是在device这个界面底下&#xff0c;我们首先要选择我们要抓的设备。 尝试下键盘设备 电脑键盘…

dll动态库加载失败导致程序启动报错以及dll库加载失败的常见原因分析与总结

目录 1、问题说明 2、dll库的隐式加载与动态加载 2.1、dll库的隐式加载 2.2、dll库的显式加载 3、使用Process Explorer查看进程加载的dll库信息以及动态加载的dll库有没有加载成功 3.1、使用Process Explorer查看进程加载的dll库信息 3.2、使用Process Explorer查看动态…

JAVA开源项目 旅游管理系统 计算机毕业设计

本文项目编号 T 063 &#xff0c;文末自助获取源码 \color{red}{T063&#xff0c;文末自助获取源码} T063&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析5.4 用例设计 六、核…

SpringBoot项目 | 瑞吉外卖 | 短信发送验证码功能改为免费的邮箱发送验证码功能 | 代码实现

0.前情提要 之前的po已经说了单独的邮箱验证码发送功能怎么实现&#xff1a; https://blog.csdn.net/qq_61551948/article/details/142641495 这篇说下如何把该功能整合到瑞吉项目里面&#xff0c;也就是把原先项目里的短信发送验证码的功能改掉&#xff0c;改为邮箱发送验证…

前端学习第一天笔记 HTML5 CSS初学以及VSCODE中的常用快捷键

前端学习笔记 VsCode常用快捷键列表HTML5标题标签标签之段落、换行、水平线标签之图片图片路径详解标签之超文本链接标签之文本列表标签之有序列表列表标签之无序列表标签之表格表格之合并单元格Form表单表单元素文本框 密码框 块元素与行内元素&#xff08;内联元素&#xff0…