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…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...

【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...

PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...

【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...

Redis数据倾斜问题解决
Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中,部分节点存储的数据量或访问量远高于其他节点,导致这些节点负载过高,影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...
基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解
JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用,结合SQLite数据库实现联系人管理功能,并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能,同时可以最小化到系统…...