Linux网络编程Socket通信6-Libevent移植与使用
目录
- libevent
- libevent交叉编译并移植
- libevent安装
- 安装步骤
- 测试代码
- libevent执行报错解决
- libevent_base根节点
- event_base_new
- event_base_free
- event_reinit
- event_loop循环等待事件
- event_base_loop
- event_base_dispatch
- event_base_loopexit
- event_base_loopbreak
- event事件
- event_new
- event_add
- event_del
- event_free
- levent事件监听流程
- event事件实现TCP代码
- bufferevent事件
- bufferevent_socket_new
- bufferevent_setcb
- bufferevent_enable、bufferevent_disable
- bufferevent_write
- bufferevent_read
- evconnlistener_new_bind
- bufferevent_socket_connect
- evconnlistener_free
- bufferevent事件监听流程
- bufferevent实现TCP服务器
- bufferevent实现TCP客户端
libevent
libevent是一个事件通知库;封装了reactor。Libevent支持Linux、Unix和Windows。对I/O事件、信号和定时事件提供统一的处理。使用libevent_pthread库来提供线程安全支持。基于Reactor模式实现。事件驱动、高性能、轻量级、专注于网络、跨平台、支持多种I/O多路复用技术、支持I/O,定时器和信号事件、注册事件优先级。
libevent交叉编译并移植
tar zxvf libevent-2.1.8-stable.tar.gz # 解压
cd libevent-2.1.8-stable
./configure --prefix=/xxx/install --host=arm-linux-gnueabihf
make
make install
cd /xxx/install
tar -zcvf libevent-2.1.12-install.tar.gz lib/*.so*# 将 libevent-2.1.12-install.tar.gz 复制到开发板上
# 新建文件夹:/usr/local/lib/libevent , 然后解压到该文件夹中
sudo mkdir /usr/local/lib/libevent
sudo tar -zxf ./libevent-2.1.12-install.tar.gz --strip-components 1 -C /usr/local/lib/libevent
# 开发板上添加库文件搜索路径
sudo vi /etc/ld.so.conf.d/libc.conf
# 在 /etc/ld.so.conf 文件中添加库的搜索路径
/usr/local/lib/libevent //根据自己的库路径添加
# 然后 ldconfig 生成/etc/ld.so.cache,可用ldconfig -v 查看
ldconfig
libevent安装
安装步骤
- tar zvxf libevent-2.1.8-stable.tar.gz -C ./
- cd libevent-2.1.8-stable
- sudo ./configure
- sudo make
- sudo make install
- ls -al /usr/local/lib | grep libevent
- 默认如此安装的库路径:/usr/local/lib 头文件目录:/usr/lcoal/include
- 编译的时候需要指定库名: -levent
测试代码
#include <event.h>
#include <stdio.h>int main()
{char ** methods = event_get_supported_methods();//获取libevent后端支持的方法int i =0;for(i = 0; methods[i] != NULL; i++){printf("%s\n",methods[i]);}struct event_base *base = event_base_new();printf("----%s\n",event_base_get_methods(base));return 0;
}/**
dongfang@dongfang-virtual-machine:~/NetWorkProgramming$ ./getmethods
epoll
poll
select
----epoll
*/
编译:gcc getmethods.c -o getmethods -levent
libevent执行报错解决
解决报错:error while loading shared libraries: libevent-2.1.so.6: cannot open shared object file: No such file or directory
ldd getmethods
linux-vdso.so.1 (0x00007ffdcc7dd000)
libevent-2.1.so.6 => not find
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f1f4e200000)
/lib64/ld-linux-x86-64.so.2 (0x00007f1f4e4ea000)find / -name libevent-2.1.so.6 --> 找到该文件在 /usr/local/lib下面LD_DEBUG=libs ./getmethods -v # 查看程序搜索库时的路径可发现他寻找的地址为 /usr/lib/libevent-2.1.so.6 , 所以我们需要添加软链接过去
sudo ln -s /usr/local/lib/libevent-2.1.so.6 /usr/lib/libevent-2.1.so.6
在 .libs 隐藏文件中包含全部libevent已经编译好的so文件。其中core为libevent的核心文件,libevent.so为主链接文件,会关联到其他全部so文件。在sample目录下会有已经编译好的服务器应用程序。在libevent的源码中的sample目录下面提供了很多例子。
libevent_base根节点
event_base_new
使用 libevent 函数之前需要分配一个或者多个 event_base 结构体。每个event_base 结构体持有一个事件集合**,**可以检测以确定哪个事件是激活的。**event_base_new()**函数分配并且返回一个新的具有默认设置的 event_base。函数会检测环境变量,返回一个到 event_base 的指针。如果发生错误,则返回 NULL。选择各种方法时,函数会选择 OS 支持的最快方法。
struct event_base *event_base_new(void);
event_base_free
申请到的 event_base 指针通过 event_base_free 函数进行释放。
void event_base_free(struct event_base *base)
event_reinit
如果fork出子进程,想在子进程继续使用event_base,那么子进程需要对event_base重新初始化,函数如下:
int event_reinit(struct event_base *base)
event_loop循环等待事件
event_base_loop
创建好libevent_base根节点后,需要等待事件的产生,也就是等待想要等待的事件的激活,在libevent中提供了对应的接口,类似 while(1){ epoll_wait() } 的功能函数。
int event_base_loop(struct event_base* base, int flags); flags的取值:#define EVLOOP_ONCE 0x01 只触发一次,如果事件没有被触发,那么就阻塞等待#define EVLOOP_NONBLOCK 0x02 非阻塞方式检测事件是否触发
event_base_dispatch
大多时候我们会调用另一个api,它相当于flags=0时候的event_base_loop函数
int event_base_dispatch(struct event_base *base); 该函数相当于没有设置标志位的event_base_loop。程序会一直运行下去,直到没有需要检测的事件了,或者被结束循环的api终止。
event_base_loopexit
等待一定时间后退出循环
int event_base_loopexit(struct event_base *event_base, const struct timeval *tv) struct timeval{long tv_sec;long tv_usec; };
event_base_loopbreak
直接退出循环
int event_base_loopbreak(struct event_base *event_base)
event事件
event_new
生成新事件,使用 event_new()接口创建事件。
struct event *event_new(struct event_base *base, evutil_socket_t fd, short events, event_callback_fn cb, void *arg); base: event_base根节点 fd: 上树的文件描述符 events: 监听的事件#define EV_TIMEOUT 0x01 // 超时事件#define EV_READ 0x02 // 读事件#define EV_WRITE 0x04 // 写事件#define EV_SIGNAL 0x08 // 信号事件#define EV_PERSIST 0x10 // 周期性触发#define EV_ET 0x20 // 设置边沿触发 cb: void (*event_callback_fn)(evutil_socket_t fd, short events, void *arg);回调函数 arg: 传入回调函数的参数
event_add
构造事件之后,在将其添加到 event_base 之前实际上是不能对其做任何操作的。使用event_add()将事件添加到 event_base。
int event_add(struct event *ev, const struct timeval *tv); ev:上树节点的地址 tv: NULL 永久监听,固定时间 限时等待
event_del
int event_del(struct event *ev);
event_free
void event_free(struct event *ev);
levent事件监听流程
- 创建套接字 socket -> 绑定 bind -> 监听 listen
- 创建 event_base(event_base_new) -> 初始化上树节点 event_new
- 上树 event_add -> 循环监听 event_base_dispatch
- 其他收尾等 event_base_free、close。
event事件实现TCP代码
#include <stdio.h>
#include <event.h>
#include "wrap.h"#define MAX 1000
// 用于保存accept到的各个客户端的cfd以及上树节点
struct eventfd {evutil_socket_t fd;struct event *ev;
}event_fd[MAX];void init_event_fd(void)
{int i = 0;for(i = 0;i < MAX; i++){event_fd[i].fd = -1;event_fd[i].ev = NULL;}
}void set_event_fd(evutil_socket_t fd, struct event* ev)
{int i = 0;for(i = 0; i < MAX; i++){if(event_fd[i].fd == -1)break;}if(i == MAX)exit(1);event_fd[i].fd = fd;event_fd[i].ev = ev;
}int find_event_fd(int fd)
{int i = 0;for(i = 0; i < MAX; i++){if(event_fd[i].fd == fd)break;}if(i == MAX){printf("not find fd = %d\n",fd);exit(1);}return i;
}void cfd_cb(evutil_socket_t cfd, short events, void* arg)
{char buf[1500] = "";memset(buf, 0, 1500);int n = Read(cfd, buf, sizeof(buf));if(n <= 0){perror("err or close!\n");int i = find_event_fd(cfd);event_del(event_fd[i].ev);event_free(event_fd[i].ev);Close(cfd);event_fd[i].fd = -1;event_fd[i].ev = NULL;}else{printf("%s\n",buf);Write(cfd, buf, n);}}void lfd_cb(evutil_socket_t lfd, short events, void* arg)
{struct event_base *base = (struct event_base*)arg;int cfd = Accept(lfd, NULL, NULL);if(cfd < 0){perror("Accept error");return;}struct event *ev = event_new(base, cfd, EV_READ | EV_PERSIST, cfd_cb, NULL);event_add(ev, NULL);set_event_fd(cfd, ev);
}int main(int argc, char* argv[])
{init_event_fd();// 创建套接字并绑定,返回服务器套接字int lfd = tcp4bind(8000, NULL);// 监听Listen(lfd, 128);// 创建event_basestruct event_base* base = event_base_new();// 初始化lfd上树节点struct event* ev = event_new(base, lfd, EV_READ | EV_PERSIST, lfd_cb, base);// 上树event_add(ev, NULL);// 循环监听event_base_dispatch(base);Close(lfd);event_base_free(base);return 0;
}
bufferevent事件
bufferevent 是 libevent 中的一个事件缓冲 IO,内部实现了基本 socket recv/send 操作 ,用户只需要调用 bufferevent 的 API 即可实现数据的读写。libevent的bufferevent事件对应一个文件描述符、两个缓冲区、三个回调函数。在libevent的应用层有读写缓冲区,对应底层的读写缓冲区。底层的读缓冲区数据拷贝到应用层缓冲区会触发读回调,从应用层缓冲区将数据写入底层缓冲区会触发写回调。还有一个事件回调,用于出错、断开连接等触发的事件回调。
bufferevent_socket_new
创建新的节点
struct bufferevent *bufferevent_socket_new(struct event_base *base, evutil_socket_t fd, int options); base : 根节点 fd : 文件描述符 options : enum bufferevent_options {// 释放bufferevent自动关闭底层接口fdBEV_OPT_CLOSE_ON_FREE = (1<<0),//使bufferevent能够在多线程下是安全的BEV_OPT_THREADSAFE = (1<<1),// 使事件循环中延迟运行回调BEV_OPT_DEFER_CALLBACKS = (1<<2),// 执行回调时不会在缓冲区事件上保留锁, 要求BEV_OPT_DEFER_CALLBACKS同时也被设置// 将来BEV_OPT_UNLOCK_CALLBACKS可能会被libevent作者考虑删除BEV_OPT_UNLOCK_CALLBACKS = (1<<3) }; 返回值:初始化新建节点的地址
bufferevent_setcb
设置节点回调
void bufferevent_setcb(struct bufferevent *bufev,bufferevent_data_cb readcb, bufferevent_data_cb writecb,bufferevent_event_cb eventcb, void *cbarg); bufev: 新建节点的地址 readcb: 读回调 writecb:写回调 eventcb:事件异常回调 cbarg: 传给回调函数的参数 typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx); typedef void (*bufferevent_event_cb)(struct bufferevent *bev, short what, void *ctx); what: #define BEV_EVENT_READING 0x01 /**< error encountered while reading */ #define BEV_EVENT_WRITING 0x02 /**< error encountered while writing */ #define BEV_EVENT_EOF 0x10 /**< eof file reached /对方关闭连接 */ #define BEV_EVENT_ERROR 0x20 /**< unrecoverable error encountered */ #define BEV_EVENT_TIMEOUT 0x40 /**< user-specified timeout reached */ #define BEV_EVENT_CONNECTED 0x80 /**< connect operation finished. */
bufferevent_enable、bufferevent_disable
事件回调的使能和不使能函数
int bufferevent_enable(struct bufferevent *bufev, short event); int bufferevent_disable(struct bufferevent *bufev, short event); event: EV_READ | EV_WRITE
bufferevent_write
发送数据
int bufferevent_write(struct bufferevent *bufev,const void *data, size_t size); 将data的数据写到buffer_event的写缓冲区中
bufferevent_read
接收数据
size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size); 将bufferevent的读缓冲区数据读取到data中, 同时将读取到的数据从bufferevent的读缓冲区删除。
evconnlistener_new_bind
连接侦听器,创建套接字、监听并提取
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: base根节点 cb: 提取套接字cfd后调用的回调typedef void (*evconnlistener_cb)(struct evconnlistener *evl, evutil_socket_t fd, struct sockaddr *cliaddr, int socklen, void *ptr);evl: 链接侦听器的地址fd: cfdcliaddr: 客户端的地址信息socklen: 地址信息长度ptr: 传递给回调函数参数 ptr: 传给回调的参数 flags: LEV_OPT_LEAVE_SOCKETS_BLOCKING 文件描述符为阻塞LEV_OPT_CLOSE_ON_FREE *关闭时自动释放LEV_OPT_REUSEABLE *端口复用LEV_OPT_THREADSAFE 分配锁,线程安全 backlog: -1 自动填充 sa: 绑定的地址信息 socklen: sa的大小 返回值:链接侦听器的地址
bufferevent_socket_connect
封装了底层的socket和connect接口,通过调用此函数将bufferevent事件和通信的socket进行绑定
int bufferevent_socket_connect(struct bufferevent *evl, const struct sockaddr *serv, int scoklen); evl: 新建的bufferevent节点 serv: 服务器地址 socklen: 服务器长度 1、创建节点 base = event_base_new(); int fd = socket(AF_INET, SOCK_STREAM, 0); struct bufferevent *evl = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE); struct bufferevent *evl = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); 2、封装 bufferevent_socket_connect(evl, serv, socklen);
evconnlistener_free
释放链接监听器
void evconnlistener_free(struct evconnlistener *lev);
bufferevent事件监听流程
- 创建套接字 socket -> 绑定 bind -> 监听 listen
- 创建 event_base(event_base_new) -> 初始化上树节点 bufferevent_socket_new
- 设置回调函数,设置完后会自动上树 bufferevent_setcb 、 设置事件使能 bufferevent_enable、 设置事件不使能 bufferevent_disable
- 循环监听 event_base_dispatch
- 其他收尾等 event_base_free、close。
bufferevent实现TCP服务器
参考hello-world.c更改为TCP收发数据
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#ifndef _WIN32
#include <netinet/in.h>
# ifdef _XOPEN_SOURCE_EXTENDED
# include <arpa/inet.h>
# endif
#include <sys/socket.h>
#endif#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/listener.h>
#include <event2/util.h>
#include <event2/event.h>static const char MESSAGE[] = "Hello, World!\n";static const int PORT = 9995;static void listener_cb(struct evconnlistener *, evutil_socket_t,struct sockaddr *, int socklen, void *);
static void conn_writecb(struct bufferevent *, void *);
static void conn_readcb(struct bufferevent *, void *);
static void conn_eventcb(struct bufferevent *, short, void *);
static void signal_cb(evutil_socket_t, short, void *);int main(int argc, char **argv)
{struct event_base *base;struct evconnlistener *listener;struct event *signal_event;struct sockaddr_in sin;
#ifdef _WIN32WSADATA wsa_data;WSAStartup(0x0201, &wsa_data);
#endifbase = event_base_new(); // 创建event_base根节点if (!base) {fprintf(stderr, "Could not initialize libevent!\n");return 1;}memset(&sin, 0, sizeof(sin));sin.sin_family = AF_INET;sin.sin_port = htons(PORT);// 创建链接监听器 listener_cb监听回调listener = evconnlistener_new_bind(base, listener_cb, (void *)base,LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1,(struct sockaddr*)&sin,sizeof(sin));if (!listener) {fprintf(stderr, "Could not create a listener!\n");return 1;}// 创建信号触发节点,收到信号SIGINT,调用信号signal_cbsignal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base);// 上树监听if (!signal_event || event_add(signal_event, NULL)<0) {fprintf(stderr, "Could not create/add a signal event!\n");return 1;}// 循环监听event_base_dispatch(base);// 释放链接监听器、信号节点、base根节点evconnlistener_free(listener);event_free(signal_event);event_base_free(base);printf("done\n");return 0;
}static void
listener_cb(struct evconnlistener *listener, evutil_socket_t fd,struct sockaddr *sa, int socklen, void *user_data)
{struct event_base *base = user_data;struct bufferevent *bev;// 创建一个bufferevent节点,释放自动关闭bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);if (!bev) {fprintf(stderr, "Error constructing bufferevent!");event_base_loopbreak(base);return;}// 设置回调函数并上树,写回调conn_writecb, 异常事件回调conn_eventcbbufferevent_setcb(bev, conn_readcb, conn_writecb, conn_eventcb, NULL);bufferevent_enable(bev, EV_WRITE | EV_READ); // 设置读写事件使能// bufferevent_disable(bev, EV_READ); // 设置读时间非使能// 给 cfd 发送消息 MESSAGEbufferevent_write(bev, MESSAGE, strlen(MESSAGE));
}static void
conn_writecb(struct bufferevent *bev, void *user_data)
{// 获取缓冲区的类型struct evbuffer *output = bufferevent_get_output(bev);if (evbuffer_get_length(output) == 0) { // 缓冲区中没有数据了// printf("flushed answer\n");// bufferevent_free(bev); // 释放节点,自动关闭}
}static void
conn_readcb(struct bufferevent *bev, void *user_data)
{char buff[1500] = "";int n = bufferevent_read(bev, buff, sizeof(buff)); // 读取数据printf("%s\n", buff);bufferevent_write(bev, buff, n); // 发送数据
}static void
conn_eventcb(struct bufferevent *bev, short events, void *user_data)
{if (events & BEV_EVENT_EOF) { // 如果连接已经关闭printf("Connection closed.\n");} else if (events & BEV_EVENT_ERROR) { // 如果连接发生错误printf("Got an error on the connection: %s\n",strerror(errno));/*XXX win32*/}/* None of the other events can happen here, since we haven't enabled* timeouts */bufferevent_free(bev); // 释放bev
}static void
signal_cb(evutil_socket_t sig, short events, void *user_data)
{struct event_base *base = user_data; // 接收传入的base根节点struct timeval delay = { 2, 0 };printf("Caught an interrupt signal; exiting cleanly in two seconds.\n");event_base_loopexit(base, &delay); // 2s后退出循环接听
}
bufferevent实现TCP客户端
可以监听 STDIN 、cfd 、服务器的数据等等
#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 infostruct 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;
}相关文章:
Linux网络编程Socket通信6-Libevent移植与使用
目录 libeventlibevent交叉编译并移植libevent安装安装步骤测试代码libevent执行报错解决 libevent_base根节点event_base_newevent_base_freeevent_reinit event_loop循环等待事件event_base_loopevent_base_dispatchevent_base_loopexitevent_base_loopbreak event事件event_…...
c#:委托 泛型委托的使用 泛型约束
委托 在 C# 中,delegate 是一种引用类型,它允许您定义和使用可以引用特定方法的对象。delegate 可以看作是一种函数指针,它可以在运行时动态地调用不同的方法。 以下是一个简单的例子来说明 delegate 的实际作用: // 1. 定义一…...
大数据之linux入门
一、linux是什么 linux操作系统 开发者是林纳斯-托瓦兹,出于个人爱好编写。linux是一个基于posix和unix的多用户、多任务、支持多线程和多CPU的操作系统。 Unix是20世纪70年代初出现的一个操作系统,除了作为网络操作系统之外,还可以作为单…...
MPI之MPI_Sendrecv接口以及空进程概念介绍
MPI_Sendrecv函数原型 int MPI_Sendrecv(const void *sendbuf, int sendcount, MPI_Datatype sendtype, int dest, int sendtag,void *recvbuf, int recvcount, MPI_Datatype recvtype, int source, int recvtag, MPI_Comm comm, MPI_Status *status);其中各个参数的含义如下&…...
Revit SDK:PointCurveCreation 创建点来拟合曲线
前言 这个例子通过留个例子来展示如何通过点来拟合曲线或者曲面。 内容 PointsParabola 生成抛物线的核心逻辑: double yctr 0; XYZ xyz null; ReferencePoint rp null; double power 1.2; while (power < 1.5){double xctr 0;double zctr 0;while (…...
嵌入式Linux开发实操(十五):nand flash接口开发
# 前言 flash memory,分NAND和NOR: 如果说nor flash有个特点就是能执行代码,NOR并行接口具有地址和数据总线,spi flash更是主要用于存储代码,SPI(或QSPI)NOR代码可就地执行(XiP),一般系统要求flash闪存提供相对较高的频率和数据缓存的clocking。而nand flash主要用于…...
vue2 组件库之vetur提示
当我们开发完自定义UI组件库后,在项目中使用时,想要达到以下提示效果,组件提示与属性提示,有什么解决方案呢: 事实上,这是vetur的功能,原文如下: Component Data | Vetur If a pac…...
慕课网 Go工程师 第三周 package和gomodules章节
Go包的引入: 包名前面加匿名,只引入但不使用,如果对应包有init函数,会执行init函数(初始化操作) 包名前面加. 把这个包的结构体和方法导入当前包,慎用,你不知道当前包和被引入的包用…...
【ES6】JavaScript 中的数组方法reduce
reduce() 是一个 JavaScript 中的数组方法,它会对数组的每个元素执行一个提供的 reducer 函数,将其减少到一个单一的值。 这是 reduce() 的基本用法: //(method) Array<number>.reduce(callbackfn: (previousValue: number, currentV…...
数据结构--树4.2(二叉树)
目录 一、二叉树的定义和特点 1、定义 2、特点 二、二叉树的基本形态 1、空二叉树 2、只有一个根结点 3、根结点只有左子树 4、根结点只有右子树 5、根结点既有左子树又有右子树 6、斜树 7、满二叉树 8、满二叉树和完全二叉树 三、二叉树的性质 一、二叉树的定义和…...
详解Numpy(基于jupyter notbook)
详解Numpy(基于jupyter notbook) 1.创建数组2.数据类型3.数组切片和索引4.Numpy的广播与数组操作5.数组合并与通用函数6.其他通用函数 1.创建数组 #引入numpy包,以后np就代表numpy import numpy as npanp.arange(10,30,2)#10为起点ÿ…...
uniapp实现:点击拨打电话,弹出电话号码列表,可以选择其中一个进行拨打
一、实现效果: 二、代码实现: 在uni-app中,使用uni.showActionSheet方法实现点击拨打电话的功能,并弹出相关的电话列表供用户选择。 当用户选择了其中一个电话后,会触发success回调函数,并通过res.tapInde…...
swc-loader Segmentation fault “$NODE_EXE“ “$NPM_CLI_JS“ “$@“
webpack swc swc还不是很稳定。 在swcrc 中有配置plugins 时,swc 转换 /node_modules/ 会报错。 环境 swc/cor1.3.62swc-loader0.2.3swc-plugin-vue-jsx0.2.5 解决 配两套rule,一套处理项目代码,一套处理node_modules webpack.config.js rules:…...
Leetcode78. 子集
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 回溯法 class Solution {public List<List<Integer>> subsets(int[] nums) {List…...
百度“AI智障”到AI智能体验之旅
目录 前言一、百度PLATO1.抬杠第一名2.听Ta瞎扯淡3.TA当场去世了4.智障与网友的高光时刻 二、文心一言1.设计测试用例2.随意发问3.手机端约会神器 三、体验总结:四、千帆大模型 前言 最近收到了文心一言3.5大模型的内测资格,正巧之前也体验过它的前身&q…...
R中当并行运算遇到C++函数时,让foreach+Rcpp一起工作
目录 方案一:C函数在R包中 方案二:C函数在本地,通过Rcpp::sourceCpp("fun_name.cpp")使用 方案三:将C函数写在当前脚本中 题外话:为什么要研究foreachRcpp? 本文参考: 问题:在fo…...
实现带头双向循环链表
🌈带头双向循环链表 描述:一个节点内包含两个指针,一个指向上一个节点,另一个指向下一个节点。哨兵位指向的下一个节点为头节点,哨兵位的上一个指向尾节点。 结构优势:高效率找尾节点;高效率插入…...
Mysql 表字符集变更
背景 线上有几张表的字符集是 latin1,要求换成utf8mb4。至于操作的时机则需要自行判断。 1.查看库中所有字符集为latin1的所有表 SELECTDISTINCTtable_schema,table_name,collation_name,character_set_name,CONCAT(ALTER TABLE , table_schema, ., table_name, …...
golang抓取tcp包的实现
要抓取 TCP 请求的数据包,你可以使用 golang 中的 packet 库和 pcap 库。下面是一种使用这些库来抓取 TCP 数据包的方法: 首先,确保已经安装了 pcap 库,可以使用以下命令来安装: go get -u github.com/google/gopack…...
oauth2.0第2季 分布式认证与授权实现单点登录
一 oauth介绍 1.0 疑问汇总 1.使用jwttoken进行令牌传输,资源服务器在本地怎么验证token? 1.1 oauth的基础内容 1.1.1 oauth是什么 1.1.2 oauth的角色 1.1.3 oauth的认证流程 1.1.4 oauth的4种模式 1.2 为何要用oauth2.0 1.介绍单体架构 使用ses…...
XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...
深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
C# 表达式和运算符(求值顺序)
求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如,已知表达式3*52,依照子表达式的求值顺序,有两种可能的结果,如图9-3所示。 如果乘法先执行,结果是17。如果5…...
离线语音识别方案分析
随着人工智能技术的不断发展,语音识别技术也得到了广泛的应用,从智能家居到车载系统,语音识别正在改变我们与设备的交互方式。尤其是离线语音识别,由于其在没有网络连接的情况下仍然能提供稳定、准确的语音处理能力,广…...
【前端实战】如何让用户回到上次阅读的位置?
目录 【前端实战】如何让用户回到上次阅读的位置? 一、总体思路 1、核心目标 2、涉及到的技术 二、实现方案详解 1、基础方法:监听滚动,记录 scrollTop(不推荐) 2、Intersection Observer 插入探针元素 3、基…...
python3GUI--基于PyQt5+DeepSort+YOLOv8智能人员入侵检测系统(详细图文介绍)
文章目录 一.前言二.技术介绍1.PyQt52.DeepSort3.卡尔曼滤波4.YOLOv85.SQLite36.多线程7.入侵人员检测8.ROI区域 三.核心功能1.登录注册1.登录2.注册 2.主界面1.主界面简介2.数据输入3.参数配置4.告警配置5.操作控制台6.核心内容显示区域7.检…...
