开源事件通知库libevent及网络连接管理模块bufferevent详解

目录

1、libevent介绍

1.1、什么是libevent?

1.2、libevent特点

1.3、网络连接管理模块bufferevent

2、bufferevent有什么用?

3、bufferevent的整体设计与实现细节

3.1、整体概况

3.2、evbuffer与bufferevent

3.3、defer callback

4、bufferevent的使用方法

4.1、创建和销毁bufferevent

4.2、设置bufferevent事件回调函数

4.3、启用或禁用bufferevent

4.4、读写数据

4.5、设置bufferevent选项

5、使用bufferevent时的细节问题

5.1、tcp连接断开处理

5.2、心跳处理

5.2.1、增加定时器事件

5.2.2、利用bufferevent的超时机制

5.3、高低水位的使用


C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125529931C/C++基础与进阶(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_11931267.htmlVC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/124272585C++软件分析工具从入门到精通案例集锦(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/131405795开源组件及数据库技术(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_12458859.html网络编程与网络问题分享(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_2276111.html

1、libevent介绍

1.1、什么是libevent?

       libevent是一个用C语言实现的、基于事件驱动(event-driven)的轻量级高性能开源网络库,适用于Windows、Linux、bsd等多种平台,内部使用select、epoll、kqueue等系统调用管理事件机制。著名的用于apache的php缓存库memcached也是基于libevent实现的。

       如果你将要开发的应用程序需要支持以上所列出的平台中的两个以上,那么建议你采用这个库,即使你的应用程序只需要支持一个平台,选择libevent也是有好处的,因为它可以根据编译/运行环境切换底层的事件驱动机制,这既能充分发挥系统的性能,又增加了软件的可移植性。

       它封装并且隔离了事件驱动的底层机制,除了一般的文件描述符读写操作外,它还提供有读写超时、定时器和信号回调,另外,它还允许为事件设定不同的优先级,当前版本的libevent还提供dns和http协议的异步封装,这一切都让这个库尤其适合于事件驱动应用程序的开发。

1.2、libevent特点

       libevent有几个显著的特点:

  • 事件驱动(event-driven),高性能;
  • 轻量级,专注于网络,不如 NGINX 那么臃肿庞大;
  • 源代码相当精炼、易读;
  • 跨平台,支持 Windows、Linux、*BSD和Mac OS,虽说Windows支持不怎么好;
  • 支持多种I/O多路复用技术,select、epoll、poll、dev/poll、select、kqueue、evports等;
  • 支持I/O,定时器和信号等事件;
  • 采用Reactor设计模式;
  • 支持HTTP(S),DNS解析。

       libevent是用于编写高速可移植非阻塞IO应用的库,其设计目标是:

  • 可移植性:使用libevent编写的程序应该可以在libevent支持的所有平台上工作。即使没有好的方式进行非阻塞IO,libevent也支持一般的方式,让程序可以在受限的环境中运行。
  • 高性能:libevent尝试使用每个平台上最高速的非阻塞IO实现,并且不引入太多的额外开销。
  • 便捷:无论何时,最自然的使用libevent编写程序的方式应该是稳定的、可移植的。
  • 可扩展性:libevent被设计为程序即使需要上万个活动套接字的时候也可以良好工作。

1.3、网络连接管理模块bufferevent

       一般通过libevent进行网络编程,都是将一个socket的fd与一个event进行绑定,并自行维护一个buffer用于存储从socket上接收的数据,同时可能也用于待发送数据的缓存。然后通过可读可写事件从socket上收取数据写入缓存并进行相应处理,或者将缓存中的数据通过socket发送。

       libevent为这种带缓存的IO模式提供了一种通用的机制,那就是bufferevent。bufferevent主要用于管理基于流的网络连接,提供了缓冲、超时、流控等功能。一个bufferevent包含了一个底层传输的fd(通常为socket),一个输入buffer和一个输出buffer,并且bufferevent已经帮我们完成了从socket上接收数据写入输入buffer,同时从输出buffer中取出数据通过socket发送,当输入输出缓存中的数据达到一定量时调用我们设置的回调函数。这样使得我们可以更加关注数据的处理。

2、bufferevent有什么用?

       bufferevent的主要作用有:

  • 读写缓冲区管理:bufferevent为我们提供了缓冲区管理的功能,可以帮助我们处理读写缓冲区的分配、管理以及数据传输的同步与异步操作。
  • 数据处理:bufferevent可以将底层的数据读写操作转化为事件的回调函数,从而使得数据的读写操作可以被更加灵活的处理。
  • 操作简单:bufferevent的使用非常简单,只需要注册事件回调函数,就可以开始读写操作,无需进行额外的操作。

总的来说 ,有了bufferevent用户就可以不用处理底层的I/O操作,直接在bufferevent中读或者写数据就行。


         在这里,给大家重点推荐一下我的几个热门畅销专栏:

专栏1:(该专栏订阅量已达到420多个,有很强的实战参考价值,广受好评!专栏文章持续更新中,预计更新到200篇以上!)

C++软件调试与异常排查从入门到精通系列文章汇总icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125529931

本专栏根据近几年C++软件异常排查的项目实践,系统地总结了引发C++软件异常的常见原因以及排查C++软件异常的常用思路与方法,详细讲述了C++软件的调试方法与手段,以图文并茂的方式给出具体的实战问题分析实例,带领大家逐步掌握C++软件调试与异常排查的相关技术,适合基础进阶和想做技术提升的相关C++开发人员!

专栏中的文章均是通过项目实战总结出来的(通过项目实战积累了大量的异常排查素材和案例),有很强的实战参考价值!专栏文章还在持续更新中,预计文章篇数能更新到200篇以上!

专栏2: 

C/C++基础与进阶(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_11931267.html

以多年的开发实战为基础,总结并讲解一些的C/C++基础与进阶内容,以图文并茂的方式对相关知识点进行详细地展开与阐述!专栏涉及了C/C++领域的多个方面的内容,同时给出C/C++及网络方面的常见笔试面试题,并详细讲述Visual Studio常用调试手段与技巧!

专栏3: 

开源组件及数据库技术icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_12458859.html

以多年的开发实战为基础,分享一些开源组件及数据库技术!


3、bufferevent的整体设计与实现细节

3.1、整体概况

       bufferevent的结构体定义如下:

从bufferevent结构体的构成可以看出,bufferevent中包含了读,写两个事件,这两个事件的回调函数分别为bufferevent_readcb和bufferevent_writecb。

       bufferevent同时还包含了输入输出两个缓存区,以及读、写、事件回调函数的指针,高低水位的设置,事件驱动的句柄等。当触发可读可写事件后,回调bufferevent_readcb和bufferevent_writecb,在这里完成从fd上的数据收发,然后根据收发结果及高低水位的设置等来进行不同回调处理。

3.2、evbuffer与bufferevent

       bufferevent采用evbuffer作为输入输出缓存,evbuffer的实现如下所示:

evbuffer像是一个字节队列,在队列的末尾写入数据,在队列的头取数据。evbuffer具体实现则是一个链表,链表中的每个节点都是一块连续的内存块。往evbuffer写数据时(evbuffer_add等函数),evbuffer内部动态创建链表节点,并紧凑的写入数据(一个节点写满,再写另外一个节点),从evbuffer中删除数据时(evbuffer_remove/evbuffer_drain),从链表头部节点开始读取,当一个节点的数据被全部读取后删除该节点,如果未读取完,则用标示记录数据已读取(删除)的部分。对于这种头部有数据被标识为读取(删除)的节点,再次写入数据时,可能会进行调整,即将数据部分整体往前拷贝移动,然后再继续写入数据。

3.3、defer callback

       在创建bufferevent时,可以设置不同的选项,其中一个是BEV_OPT_DEFER_CALLBACKS,这意味着延迟进行回调。

       所谓延迟回调,是将该事件延迟等到本次事件循环中,所有active事件都处理完成后再进行该事件的处理。在event_base中,有一个active事件队列,一个defer事件队列。事件循环时,遍历active事件队列并进行相应的处理,当发现某个事件时需要延迟处理时,将该事件放到defer事件队列中,继续后续active事件的处理,等active事件队列中的事件都处理完成后,再处理defer队列中的事件。

       对于bufferevent来说,当fd上有数据可读时,其实是先进行了一次回调(bufferevent_readcb),这个回调函数中判断是否需要延迟处理,如果不需要延迟则直接调用我们设置的回调函数,如果需要延迟,则等libevent处理完其他的active事件后再次调用bufferevent的回调函数,再由该回调函数调用我们设置的回调函数。


         在这里,给大家重点推荐一下我的几个热门畅销专栏:

专栏1:(该专栏订阅量已达到400多个,有很强的实战参考价值,广受好评!专栏文章持续更新中,预计更新到200篇以上!)

C++软件调试与异常排查从入门到精通系列文章汇总https://blog.csdn.net/chenlycly/article/details/125529931

本专栏根据近几年C++软件异常排查的项目实践,系统地总结了引发C++软件异常的常见原因以及排查C++软件异常的常用思路与方法,详细讲述了C++软件的调试方法与手段,以图文并茂的方式给出具体的实战问题分析实例,带领大家逐步掌握C++软件调试与异常排查的相关技术,适合基础进阶和想做技术提升的相关C++开发人员!

专栏中的文章均是通过项目实战总结出来的(通过项目实战积累了大量的异常排查素材和案例),有很强的实战参考价值!专栏文章还在持续更新中,预计文章篇数能更新到200篇以上!

专栏2: 

C/C++基础与进阶(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/category_11931267.html

以多年的开发实战为基础,总结并讲解一些的C/C++基础与进阶内容,以图文并茂的方式对相关知识点进行详细地展开与阐述!专栏涉及了C/C++领域的多个方面的内容,同时给出C/C++及网络方面的常见笔试面试题,并详细讲述Visual Studio常用调试手段与技巧!

专栏3: 

开源组件及数据库技术https://blog.csdn.net/chenlycly/category_12458859.html

以多年的开发实战为基础,分享一些开源组件及数据库技术!


4、bufferevent的使用方法

       使用bufferevent主要有以下几个步骤。

4.1、创建和销毁bufferevent

       使用libevent创建bufferevent非常简单。首先,创建一个event_base对象和一个套接字描述符,然后使用bev_socket_new或bev_bufferevent_new函数创建一个新的bufferevent。在不再需要时,可以使用bev_free函数释放bufferevent。 

struct event_base *base = event_base_new();
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct bufferevent *bev = bufferevent_socket_new(base, sockfd, BEV_OPT_CLOSE_ON_FREE);

//释放
bufferevent_free(bev);
event_base_free(base);

4.2、设置bufferevent事件回调函数

       bufferevent需要处理不同类型的事件,例如读取数据、写入数据、错误、超时等。使用bufferevent_setcb函数设置回调函数来处理这些事件。在回调函数中,可以读取或写入数据,或者关闭bufferevent。

void my_read_cb(struct bufferevent *bev, void *ctx)
{
    struct evbuffer *input = bufferevent_get_input(bev);
    // 从输入读取数据
}

void my_write_cb(struct bufferevent *bev, void *ctx)
{
    struct evbuffer *output = bufferevent_get_output(bev);
    // 将数据写入到输出
}

void my_event_cb(struct bufferevent *bev, short events, void *ctx)
{
    if (events & BEV_EVENT_ERROR)
    {
        // 处理错误事件
        bufferevent_free(bev);
    }
}


bufferevent_setcb(bev, my_read_cb, my_write_cb, my_event_cb, NULL);

4.3、启用或禁用bufferevent

       可以使用bufferevent_enable和bufferevent_disable函数启用或禁用bufferevent的读、写或事件操作。如果需要暂停读或写数据,则可以使用BEV_SUSPEND读或写操作,然后使用BEV_RESUME恢复它们。

// 禁用读取操作
bufferevent_disable(bev, EV_READ);

// 启用写入操作
bufferevent_enable(bev, EV_WRITE);

4.4、读写数据

       使用bufferevent_read和bufferevent_write函数读取和写入数据。bufferevent可以在内部缓冲区中缓存数据,也可以直接读取或写入套接字。

// 读数据
bufferevent_read(bev, buffer, buffer_size);

// 写数据
bufferevent_write(bev, buffer, buffer_size);

4.5、设置bufferevent选项

       可以使用bufferevent_setwatermark和bufferevent_settimeout函数设置bufferevent的选项。其中,bufferevent_setwatermark设置读取和写入缓冲区的低水位和高水位。bufferevent_settimeout设置超时时间。

// 设置watermark
bufferevent_setwatermark(bev, EV_READ, lowmark, highmark);
bufferevent_setwatermark(bev, EV_WRITE, lowmark, highmark);

// 设置 timeout
struct timeval tv = {5, 0}; // 5秒
bufferevent_set_timeouts(bev, &tv, NULL);

这里详细说一下bufferevent_setwatermark函数。在该函数中,第二个参数是 events,它表示在何时触发回调函数。具体来说,EV_READ 和 EV_WRITE 是事件标志,它们分别代表读事件和写事件。

EV_READ 用于在读操作时触发回调函数。
EV_WRITE 用于在写操作时触发回调函数。

       在这里,我们可以理解为:lowmark 和 highmark 是在读取数据时缓冲区的低水位和高水位。
EV_READ 表示在缓冲区的读取操作中触发回调函数。因此,bufferevent_setwatermark(bev, EV_READ, lowmark, highmark) 的作用是:当读取缓冲区的数据量达到 lowmark 或 highmark 时,触发读操作的回调函数。

关于低水位和高水位:当写入缓冲区的数据量超过了高水位时,bufferevent 将停止发送数据,等待缓冲区中的数据被消费。当写入缓冲区中的数据量低于低水位时,bufferevent 又会恢复写入数据,向下游发送数据,以保证高效的数据传输。

5、使用bufferevent时的细节问题

5.1、tcp连接断开处理

       对于客户端来说,如果仅有bufferevent这么一个事件,那么当tcp连接断开时,调用回调函数后会退出事件循环(event_base_loop)。因为bufferevent感知tcp连接断开后会删除相关的事件,这个时候事件驱动中没有任何事件,于是退出循环。

       在官网的教程中看到可以对event_base设置选项EVLOOP_NO_EXIT_ON_EMPTY保证没有等待事件时也不会退出事件循环,但是在最新稳定版本中(libevent-2.0.21-stable)没有该选项设置,在2.1.x-alpha中才有该选项。我们可以采用增加定时器事件的方式来处理断链后不退出事件循环,甚至进一步实现断链重连的功能, 这个定时器事件可以是断链后在回调函数中动态增加,也可以一开始就增加一个持久的定时器事件,检测连接状态并触发向服务器重连。例如:

int g_nState;
 
//定时器事件回调函数
void handle_timeout(int nSock, short sWhat, void * pArg)
{
    if( 0 == g_nState )
    {
        struct bufferevent * pBufferEvent = (struct bufferevent *)pArg;
 
        struct sockaddr_in tSockAddr;
        memset(&tSockAddr, 0, sizeof(tSockAddr));
        tSockAddr.sin_family = AF_INET;
        tSockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
        tSockAddr.sin_port = htons(50000);
 
        bufferevent_socket_connect(pBufferEvent, (struct sockaddr*)&tSockAddr, sizeof(tSockAddr));
    }
}
 
void event_callback(struct bufferevent * pBufEv, short sEvent, void * pArg)
{
    //成功连接 状态变更
    if( BEV_EVENT_CONNECTED == sEvent )
    {
        bufferevent_enable( pBufEv, EV_READ );
        g_nState = 1;
    }
 
    //出现错误
    if( 0 != (sEvent & (BEV_EVENT_ERROR)) )
    {
        //关闭fd并更改状态
        int fd = bufferevent_getfd(pBufEv);
        if( fd > 0 )
        {
            evutil_closesocket(fd);
        }
        bufferevent_setfd(pBufEv, -1);
        g_nState = 0;
    }
}
 
int main( void )
{
    ...
    //增加PERSIST的定时器事件
    struct event eTimeout;
    struct timeval tTimeout = {10, 0};
    //回调函数的参数为bufferevent
    event_assign(&eTimeout, pEventBase, -1, EV_PERSIST, handle_timeout, pBufferEvent);
    evtimer_add(&eTimeout, &tTimeout);
 
    ...
}

       这里需要注意的是,重连之前最好先关闭bufferevent中fd,或者直接对bufferevent进行释放并重新创建一个新的bufferevent。如果是直接释放bufferevent再次新建,那么在创建bufferevent时记得设置BEV_OPT_CLOSE_ON_FREE参数,这样在释放bufferevent时会对fd进行关闭,从而不会出现fd泄漏。

不设置该参数,通过bufferevent_setfd传入fd,释放bufferevent后自动关闭fd也是一种处理方式。

5.2、心跳处理

       通常,客户端与服务端之间都有心跳检测,以检测tcp链路是否正常,那么通过bufferevent开发的客户端或者服务端完成心跳检测功能可以有这么几种实现方式。

5.2.1、增加定时器事件

       前面提到了可以增加持久的定时器事件来检测状态并触发断链重连,当然我们也可以利用这个定时器事件来完成定时发送心跳包的功能。个人觉得这种方式不太好的一点是:需要有一种机制让定时器事件的回调处理函数获取bufferevent的句柄,例如作为定时器事件回调函数的参数,这样才能将心跳包的数据写入该bufferevent并通过fd发送,但两种事件搅合在一起感觉会有些混乱。

5.2.2、利用bufferevent的超时机制

       bufferevent可以为读写设置超时时间,我们可以设置读超时来完成定时发送心跳包的功能。在事件回调处理函数中处理BEV_EVENT_TIMEOUT|BEV_EVENT_READING事件,然后将心跳包写入输出缓存。这种方式有一点需要注意:bufferevent触发超时事件后会将对应的可读/可写事件删除,我们在处理完超时事件后需要重新注册一下对应的事件(bufferevent_enable)。例如:

{
    if( BEV_EVENT_CONNECTED == sEvent )
    {
        bufferevent_enable( pBufEv, EV_READ );
        //设置读超时时间  10s
        struct timeval tTimeout = {10, 0};
        bufferevent_set_timeouts( pBufEv, &tTimeout, NULL);
    }
 
    if(0 != (sEvent & (BEV_EVENT_TIMEOUT|BEV_EVENT_READING)) )
    {
        //发送心跳包
        ...
        //重新注册可读事件
        bufferevent_enable(pBufEv, EV_READ);
    }
    ...
    return;
}

5.3、高低水位的使用

       默认情况下,bufferevent从fd上接收到任何数据并写入输入缓存区时,就会回调交给我们进行处理。而我们的客户端和服务端通信时都会遵循一定的格式(数据包协议),比如固定长度的包头,然后从包头中获取包体的数据长度,然后等包体的数据都接收完成后再进行实际处理。在这种情况下,我们可以设置读的低水位减少回调的次数,bufferevent会等输入缓存区中的数据长度超过最低水位后,才回调我们的函数进行业务处理。

       同样,设置写的低水位,表示只有输出缓存区的数据低于最低水位后,调用写回调函数进行相应处理。高水位的设置可用于进行传输速率的控制,例如设置读的高水位时,当输入缓存区中的数据长度超过高水位时,会处于“挂起”状态,即不再从fd上读取数据,直到输入缓存区中的数据低于高水位。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

听说英伟达和诺和诺德要共建医药研发超算,超算安腾默默笑了

继黄仁勋公开发表“生命科学才是未来”的观点后,英伟达押注生物计算赛道又有新动作。日前,诺和诺德基金会宣布将出资和英伟达合作,在丹麦建造一台专注于生成式AI应用的超级计算机,以推动医疗保健、生命科学和绿色转型领域的研究与…

【JavaSE进阶】08-反射机制 09-注解

1 反射机制 a) 反射的基本概念 b) Java中的类反射 c) 安全性和反射 d) 反射的两个缺点 1.1 反射的基本概念 反射的概念是由 Smith 在 1982 年首次提出的,主要是指程序可以访问、检测和修改它本身状 态或行为的一种能力, 并能根据自身行为的状态和结果&#…

六、项目发布 -- 2. 数据库环境准备

之前我们是采用mock方式获取这些接口,也就是这些接口的数据其实是固定的,现在我们将从数据库中来获取这些数据并且在界面上进行展示。 1、数据库环境准备 到MYSQL官网上,主要下载服务端,社区版是免费的,安装好MYSQL …

结构体(C语言)

“点赞,留言,收藏,关注” 就是对阿林最大的支持 1.自定义类型 什么是自定义类型?C语言中有一些自带的数据类型,比如说char,int,float,double,long等数据类型就是C语言的…

目标检测——标注鱼类数据集

一、重要性及意义 鱼类的检测在多个领域都表现出其重要性和意义。以下是几个主要方面的阐述: 首先,从食品安全和营养价值的角度来看,鱼类作为人们日常生活中的重要蛋白质来源,其质量和安全性备受关注。鱼类营养成分检测能够评估…

C++异步回调示例:多线程执行任务,主线程通过回调监测任务状态

1、回调函数 回调函数定义:把函数的指针或者地址作为参数传递给另一个参数,当这个指针被用来调用其所指向的函数时,那么这就是一个回调的过程,这个被回调的函数就是回调函数。回调函数不是由该函数的实现方直接调用,而…

反转二叉树(力扣226)

解题思路:用队列进行前序遍历的同时把节点的左节点和右节点交换 具体代码如下: class Solution { public:TreeNode* invertTree(TreeNode* root) {if (root NULL) return root;swap(root->left, root->right); // 中invertTree(root->left)…

oracle 数据库 迁移 mysql

将 Oracle 数据库迁移到 MySQL 是一项复杂的任务,因为这两种数据库管理系统具有不同的架构、语法和功能。下面是一个基本的迁移步骤,供你参考: 步骤一:评估和准备工作 1.评估数据库结构:仔细分析 Oracle 数据库的结构…

让15万的车也配激光雷达,速腾发布中长距「千元机」MX

‍作者 |老缅 编辑 |德新 4月15日,国内头部激光雷达公司速腾聚创发布了新一代中长距激光雷达MX。 相比较其产品配置,最令人惊喜的是它的价格。 「MX将以低于200美元的价格作为基础,实现第一个项目的量产。」速腾聚创CEO邱纯潮在发布会现场…

Python连接Oracle数据库问题解决及Linux服务器操作知识

背景说明 最近在做一个视频分析的项目,然后需要将视频分析的数据写入到oracle数据库,直接在服务器上测试数据库连接的时候出现了这个bug提示,自己通过不断的研究探讨,最终把这个问题成功进行了解决,在这里进行一下记录…

Avi Wigderson:理论计算机科学的巨人

🏆个人专栏 🤺 leetcode 🧗 Leetcode Prime 🏇 Golang20天教程 🚴‍♂️ Java问题收集园地 🐍 Python工具 🌴 成长感悟 欢迎大家观看,不执着于追求顶峰,只享受探索过程 A…

LeetCode-热题100:98. 验证二叉搜索树

题目描述 给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下: 节点的左子树只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。 示例 1&#x…

Java项目:基于SSM框架实现的心遗非遗文创电商平台(源码+数据库)

一、项目简介 本项目是一套基于SSM框架实现的心遗非遗文创电商平台 包含:项目源码、数据库脚本等,该项目附带全部源码可作为毕设使用。 项目都经过严格调试,eclipse或者idea 确保可以运行! 该系统功能完善、界面美观、操作简单、…

Linux_CentOS7/8系统 - 关闭图形界面新增用户机制手册

Linux_CentOS7/8系统 - 关闭图形界面新增用户机制手册 在系统完成图形界面安装后重新启动后第一次登入,在图形界面会有新增用户页面,那如果取消关闭可以按以下操作: CTRLALTF2 root账号登录 yum remove gnome-initial-setup -y init 3 init …

微信小程序公共组件封装使用

1.在components目录下创建公共组件,以navbar为例 2.完成组件功能 3.调用,如果很多地方都会用到,建议放全局,如果不是则放在需要引用的文件中 3.1全局引用,在app.json做全局引用配置 3.2局部引用,在需要引入…

【C++庖丁解牛】C++11---统一的列表初始化 | auto | decltype | nullptr | STL中一些变化

🍁你好,我是 RO-BERRY 📗 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 🎄感谢你的陪伴与支持 ,故事既有了开头,就要画上一个完美的句号,让我们一起加油 目录 1. C11简介2. 统一的列表…

3.1 海思SS928开发 - 烧写工具 - ToolPlatform 安装及配置

3.1 烧写工具 - ToolPlatform 安装及配置 ToolPlatform 安装 进入到开发虚拟机,将文件 ~/hiss928/sdk/ema_2.0.2.2/pc/ToolPlatform/ToolPlatform-1.0.11-win32-x86_64.zip 拷贝至 PC 上。PC 要求安装了 win7 及以上的操作系统。解压压缩包 ToolPlatform-1.0.11-w…

49-PCIE转网口电路设计

视频链接 PCIE转网口电路设计01_哔哩哔哩_bilibili PCIe转网口电路设计 1、PCIE转网口电路设计基本介绍 pcie转网口的设计,一般有intel (i350)和网讯(wx1860)两种方案。 2、PCIE转网口的方案 2.1、I350 2.2、WX1860 (网迅) 国产化&#…

java文件夹文件比较工具

import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.HashSet; import java.util.Set;public class FolderFileNames {public static void main(String[] args) {// 假设您要读取的文件夹路径是 &q…

强大的系统监测工具 iStat Menus for mac最新中文激活版

iStat Menus for Mac是一款功能强大的系统监控工具,专为Mac用户设计,旨在帮助用户全面了解电脑的运行状态,提高电脑的性能和稳定性。 iStat Menus for mac最新中文激活版下载 该软件可以实时监测CPU使用率、内存占用、网络速度、硬盘活动等各…