Linux网络编程:libevent事件通知库

news2024/11/18 18:26:30

文章目录:

一:libevent库

二:libevent框架

1.常规事件event

1.1 创建事件event(event_new) 

1.2 添加事件到 event_base(event_add) 

1.3 从event_base上摘下事件(event_del)

1.4 销毁事件(event_free)

1.5 未决和非未决

read_fifo.c

write_fifo.c

2.带缓冲区的事件bufferevent

2.1 带缓冲区的事件 bufferevent

2.2 创建、销毁bufferevent(bufferevent_socket_new 、bufferevent_socket_free)

2.3 给bufferevent设置回调(bufferevent_setcb)

2.4 启动、关闭 bufferevent的 缓冲区(bufferevent_enable、bufferevnet_disable)

三:网络通信 

1.服务端

1.1 创建和释放监听服务器(evconnlistener_new_bind、evconnlistener_free) 

1.2 服务器端 libevent 创建TCP连接流程

ev_server.c

2.客户端

2.1 连接客户端(bufferevent_socket_connect) 

2.2 Libevent实现TCP客户端流程 

ev_client.c


一:libevent库

libevent官网

libevent库
	开源;精简;跨平台(Windows、Linux、maxos、unix);专注于网络通信


源码包安装:  参考 README、readme

	./configure		    检查安装环境 生成 makefile
	make			    生成 .o 和 可执行文件
	sudo make install	将必要的资源cp置系统指定目录

	进入 sample 目录,运行demo验证库安装使用情况
	编译使用库的 .c 时,需要加 -levent 选项
	库名 libevent.so --> /usr/local/lib   查看的到


特性:
	基于“事件”异步通信模型。--- 回调

二:libevent框架

libevent框架:

	1. 创建 event_base		(乐高底座)

		struct event_base *event_base_new(void);

		struct event_base *base = event_base_new();


	2. 创建 事件evnet	   (积木)

		常规事件       event	   --> event_new(); 

		带缓冲区的事件 bufferevent --> bufferevent_socket_new();


	3. 将事件 添加到 base上	

		int event_add(struct event *ev, const struct timeval *tv)


	4. 循环监听事件满足

		int event_base_dispatch(struct event_base *base);

			event_base_dispatch(base);


	5. 释放 event_base

		event_base_free(base);

1.常规事件event

1.1 创建事件event(event_new) 

创建事件event:

	struct event *ev;

	struct event *event_new(struct event_base *base,evutil_socket_t fd,short what,event_callback_fn cb;  void *arg);

		base: event_base_new()返回值

		fd: 绑定到 event 上的 文件描述符

		what:对应的事件(r、w、e)
			EV_READ		一次 读事件
			EV_WRTIE	一次 写事件
			EV_PERSIST	持续触发。 结合 event_base_dispatch 函数使用,生效

		cb:一旦事件满足监听条件,回调的函数



		typedef void (*event_callback_fn)(evutil_socket_t fd,  short,  void *)	

		arg: 回调的函数的参数

		返回值:成功创建的 event

1.2 添加事件到 event_base(event_add) 

添加事件到 event_base

	int event_add(struct event *ev, const struct timeval *tv);

		ev: event_new() 的返回值

		tv:NULL

1.3 从event_base上摘下事件(event_del)

从event_base上摘下事件				【了解】

	int event_del(struct event *ev);

		ev: event_new() 的返回值

1.4 销毁事件(event_free)


销毁事件

	int event_free(struct event *ev);

		ev: event_new() 的返回值

1.5 未决和非未决

未决和非未决:
	非未决: 没有资格被处理
	未  决:有资格被处理,但尚未被处理

	event_new --> event ---> 非未决 --> event_add --> 未决 --> dispatch() && 监听事件被触发 --> 激活态 

	--> 执行回调函数 --> 处理态 --> 非未决 event_add && EV_PERSIST --> 未决 --> event_del --> 非未决

read_fifo.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#include <event2/event.h>

// 对操作处理函数
void read_cb(evutil_socket_t fd, short what, void *arg)
{
    // 读管道
    char buf[1024] = {0};
    
    int len = read(fd, buf, sizeof(buf));
    
    printf("read event: %s \n", what & EV_READ ? "Yes" : "No");
    printf("data len = %d, buf = %s\n", len, buf);
    
    sleep(1);
}


// 读管道
int main(int argc, const char* argv[])
{
    unlink("myfifo");

    //创建有名管道
    mkfifo("myfifo", 0664);

    // open file
    //int fd = open("myfifo", O_RDONLY | O_NONBLOCK);
    int fd = open("myfifo", O_RDONLY);
    if(fd == -1)
    {
        perror("open error");
        exit(1);
    }

    // 创建个event_base
    struct event_base* base = NULL;
    base = event_base_new();

    // 创建事件
    struct event* ev = NULL;
    ev = event_new(base, fd, EV_READ | EV_PERSIST, read_cb, NULL);

    // 添加事件
    event_add(ev, NULL);

    // 事件循环
    event_base_dispatch(base);  // while(1) { epoll();}

    // 释放资源
    event_free(ev);
    event_base_free(base);
    close(fd);
    
    return 0;
}

write_fifo.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#include <event2/event.h>

// 对操作处理函数
void write_cb(evutil_socket_t fd, short what, void *arg)
{
    // write管道
    char buf[1024] = {0};
    
    static int num = 0;
    sprintf(buf, "hello,world-%d\n", num++);
    write(fd, buf, strlen(buf)+1);
    
    sleep(1);
}


// 写管道
int main(int argc, const char* argv[])
{
    // open file
    //int fd = open("myfifo", O_WRONLY | O_NONBLOCK);
    int fd = open("myfifo", O_WRONLY);
    if(fd == -1)
    {
        perror("open error");
        exit(1);
    }

    // 写管道
    struct event_base* base = NULL;
    base = event_base_new();

    // 创建事件
    struct event* ev = NULL;
    // 检测的写缓冲区是否有空间写
    //ev = event_new(base, fd, EV_WRITE , write_cb, NULL);
    ev = event_new(base, fd, EV_WRITE | EV_PERSIST, write_cb, NULL);

    // 添加事件
    event_add(ev, NULL);

    // 事件循环
    event_base_dispatch(base);

    // 释放资源
    event_free(ev);
    event_base_free(base);
    close(fd);
    
    return 0;
}

2.带缓冲区的事件bufferevent

2.1 带缓冲区的事件 bufferevent

带缓冲区的事件 bufferevent

	#include <event2/bufferevent.h> 

	read/write 两个缓冲. 借助 队列



原理: bufferent利用队列实现两个缓冲区(数据读走就没, FIFO);

读: 有数据, 读回调函数被调用, 使用bufferevent_read()读数据;

写: 使用bufferevent_write, 向写缓冲中写数据, 该缓冲区中有数据自动写出, 写完后, 回调函数被调用(鸡肋);

2.2 创建、销毁bufferevent(bufferevent_socket_new 、bufferevent_socket_free)

创建bufferevent:
    struct bufferevent* bufferevent_socket_new(struct event_base* base,
                                           evutil_socket_t fd,
                                           enum bfferevent_options options)

    base: 基事件, event_base_new函数的返回值;
    fd:封装到bufferevent内的fd(绑定在一起);
    enum表示枚举类型, 一般取BEV_OPT_CLOSE_ON_FREE;
    成功返回bufferevent事件对象;



销毁bufferevent:
    void bufferevent_socket_free(struct bufferevent* ev)

2.3 给bufferevent设置回调(bufferevent_setcb)

给bufferevent设置回调:
	
	对比event:	event_new( fd, callback );  					event_add() -- 挂到 event_base 上。

	
				bufferevent_socket_new(fd)					bufferevent_setcb( callback )

	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_cb{  bufferevent_read() 读数据  }
	    writecb: 设置 bufferevent 写缓冲,对应回调 write_cb {  } -- 给调用者,发送写成功通知。  可以 NULL
        eventcb: 可传NULL;
        cbarg: 回调函数的参数;



			eventcb: 设置 事件回调。   也可传NULL

				typedef void (*bufferevent_event_cb)(struct bufferevent *bev,  short events, void *ctx);

				void event_cb(struct bufferevent *bev,  short events, void *ctx)
				{

					。。。。。
				}

				events: BEV_EVENT_CONNECTED



			read 读回调函数类型(read_cb :bufferevent_read()):

				typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void*ctx);

				void read_cb(struct bufferevent *bev, void *cbarg )
				{
					.....
					bufferevent_read();   --- read();
				}


				bufferevent_read()函数的原型:

					size_t bufferevent_read(struct bufferevent *bev, void *buf, size_t bufsize);

			
			write 写回调函数类型(bufferevent_write):

				int bufferevent_write(struct bufferevent *bufev, const void *data,  size_t size); 

2.4 启动、关闭 bufferevent的 缓冲区(bufferevent_enable、bufferevnet_disable)

启动、关闭 bufferevent的 缓冲区:

	void bufferevent_enable(struct bufferevent* bufev,short events);		//启用缓冲区
    void bufferevnet_disable(struct bufferevent* bufev,short events);		//禁用	

		events的值可传入三个宏: EV_READ、EV_WRITE、EV_READ|EV_WRITE

		默认、write 缓冲是 enable、read 缓冲是 disable

			bufferevent_enable(evev, EV_READ);		-- 开启读缓冲

三:网络通信 

1.服务端

1.1 创建和释放监听服务器(evconnlistener_new_bind、evconnlistener_free) 

创建监听服务器:

	------ socket();bind();listen();accept();

	struct evconnlistener * listner

    //这一个函数可以完成`socket(),bind(),listen(),accept()`四个函数的作用
	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: event_base
	    cb: 回调函数。 一旦被回调,说明在其内部应该与客户端完成, 数据读写操作,进行通信
	    ptr: 回调函数的参数
	    flags: LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE
	    backlog: listen() 2参。 -1 表最大值
	    sa:服务器自己的地址结构体
	    socklen:服务器自己的地址结构体大小
	    返回值:成功创建的监听器

    //回调函数的类型
    typedef void (*evconnlistener_cb)(struct evconnlistener* listener,
                                 evutil_socker_t sock,
                                 struct sockaddr* addr,
                                 int len,
                                 void* ptr);

        listener:evconnlistener_new_bind函数的返回值;
        sock:用于通信的文件描述符;
        addr:客户端的地址结构;
        len:客户端地址结构的长度;
        ptr:外部ptr传进来的值;


释放监听服务器:

	void evconnlistener_free(struct evconnlistener *lev);

1.2 服务器端 libevent 创建TCP连接流程

服务器端 libevent 创建TCP连接:

    1. 创建event_base

    2. 创建bufferevent事件对象。bufferevent_socket_new()

    3. 使用bufferevent_setcb() 函数给 bufferevent的 read、write、event 设置回调函数

    4. 当监听的 事件满足时,read_cb会被调用, 在其内部 bufferevent_read()读

    5. 使用 evconnlistener_new_bind 创建监听服务器, 设置其回调函数,当有客户端成功连接时,这个回调函数会被调用

    6. 封装 listner_cb() 在函数内部。完成与客户端通信

    7. 设置读缓冲、写缓冲的 使能状态 enable、disable

    8. 启动循环 event_base_dispath()

    9. 释放连接

ev_server.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <event2/event.h>
#include <event2/listener.h>
#include <event2/bufferevent.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"); 
}


// 被回调,说明有客户端成功连接, cfd已经传入该参数内部。 创建bufferevent事件对象
//与客户端完成读写操作
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;
   // 通信操作
   // 创建添加新事件bufferevent 对象
   struct bufferevent *bev;
   bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);

   // 给bufferevent缓冲区设置回调 read、write、event
   void bufferevent_setcb(struct bufferevent * bufev,
				bufferevent_data_cb readcb,
				bufferevent_data_cb writecb,
				bufferevent_event_cb eventcb,
				void *cbarg );
   //设置回调函数
   bufferevent_setcb(bev, read_cb, write_cb, event_cb,NULL,NULL);
  
   //启动 read 缓冲区的 使能状态
   bufferevent_enable(bev, EV_READ);
}


int main(int argc, const char* argv[])
{

    // 定义服务器地址结构init server 
    struct 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);

    // 创建event_base
    struct event_base* base;
    base = event_base_new();
    // 创建套接字
    // 绑定
    // 创建服务器监听器:接收连接请求
    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));

    //启动监听循环
    event_base_dispatch(base);

    //销毁event_base
    evconnlistener_free(listener);
    event_base_free(base);

    return 0;
}

2.客户端

2.1 连接客户端(bufferevent_socket_connect) 

连接客户端:
	socket();connect();

	int bufferevent_socket_connect(struct bufferevent *bev, struct sockaddr *address, int addrlen);
		    bev: bufferevent 事件对象(封装了fd)
		    address、len:等同于 connect() 参2/3

2.2 Libevent实现TCP客户端流程 

Libevent实现TCP客户端流程
    1.创建event_basev
    2.使用bufferevnet_socket_new()创建一个用跟服务器通信的 bufferevnet事件对象
    3.使用bufferevnet_socket_connect()连接服务器
    4.使用bufferevent_setcb()给 bufferevnet对象的 read、write、event设置回调
    5.设置bufferevnet 对象的读写缓冲区enable / disable
    6.接受、发送数据bufferevent_read() / bufferevent_write()
    7.启动循环监听event_base_dispatch
    8.释放资源

ev_client.c

#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 info
    struct 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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Desnet模型详解

模型介绍 DenseNet的主要思想是密集连接&#xff0c;它在卷积神经网络&#xff08;CNN&#xff09;中引入了密集块&#xff08;Dense Block&#xff09;&#xff0c;在这些块中&#xff0c;每个层都与前面所有层直接连接。这种设计可以让信息更快速地传播&#xff0c;有助于解…

xargs命令解决“Argument list too long”

一、xargs命令概述 xargs命令是给其他命令传递参数的一个过滤器&#xff0c;也是组合多个命令的一个工具。它擅长将标准输入数据转换成命令行参数&#xff0c;xargs能够处理管道或者stdin并将其转换成特定命令的命令参数。空格是其默认定界符&#xff0c;管道传递给xargs的输入…

J2L3x助力企业业务协同,打破部门壁垒

在现代企业中&#xff0c;协同办公已经成为了一个关键的话题。在过去的几年里&#xff0c;我们已经看到了许多团队努力打破内部部门之间的壁垒&#xff0c;以更为高效的方式来协同工作。今天&#xff0c;我们要谈的是一种叫做J2L3x的企业沟通工具&#xff0c;这是一个旨在帮助团…

浅谈APP自动化测试工具的优势和应用

随着移动应用市场的迅速发展&#xff0c;APP的质量和性能变得至关重要。为了确保APP的稳定性和用户体验&#xff0c;自动化测试工具成为开发者和测试团队的关键利器。那么&#xff0c;APP自动化测试工具的优势和应用是什么?下面&#xff0c;就跟随掌控智能小编一起来看看具体介…

MySQL数据的导入导出mysqldump、mysqlimport into outfile和load data

0、概述 MySQL数据的导入导出方案通常是配套的&#xff0c;例如&#xff1a; 方案一&#xff1a;使用mysqldump导出数据&#xff0c;再使用mysql客户端导入数据 方案二&#xff1a;使用SELECT INTO OUTFILE命令导出数据&#xff0c;再使用LOAD DATA或mysqlimport导入数据 方案…

数据库第十七课-------ETL任务调度系统的安装和使用

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; ​&#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382; &#x1f389;&#x1f389;&#x1f389…

java八股文面试[数据结构]——HashMap扩容优化

知识来源&#xff1a; 【2023年面试】HashMap在扩容上做了哪些优化_哔哩哔哩_bilibili

详解numpy.random.randn函数

文章目录 正态分布函数原型参数解析该函数的注意事项示例代码示例结果 参考正态分布曲线绘制代码 numpy的random模块中的randn函数用于从“标准正态&#xff08;方差为1&#xff0c;均值为0的正态分布&#xff09;”分布返回一个&#xff08;或多个&#xff09;float类型数据。…

Git提交项目到码云或者GitHub(小白也能看懂,图文详解)

1.在gitee上搭建远程仓库&#xff0c;并与本地连接 先注册一个gitee账号 Gitee - 企业级 DevOps 研发效能平台 在码云上新建仓库&#xff0c;用来存放项目 先在本地随便一个地方执行&#xff1a; 鼠标右键、git bash here&#xff08;前提是你安装好了git&#xff09; 配置…

yolov5的txt文件转xml文件格式(详细解释与完整代码供应)

文章目录 前言一、yolov5训练数据格式介绍1、txt的类别对应说明2、txt的文件说明3、txt文件格式3、yolov5训练文件形式 二、生成xml文件代码说明1、yolov5的txt读取代码2、生成xml代码 三、yolov5的txt文件转xml文件步骤四、完整代码 前言 本文章实现yolov5的txt数据格式转xml…

Cesium 添加与原生按钮样式相同的按钮

Cesium 添加与原生按钮样式相同的按钮 原生的按钮自定义一个&#xff0c;仿生按钮 原生的按钮 自定义一个&#xff0c;仿生按钮 html <div id"cesiumContainer"><button class"btn" ref"newBtn" click"buttonClick()">&l…

python3高级编程

文章目录 1. Python网络编程1.1 服务器端代码(Server)1.2 客户端代码(Client) 2. 多线程2.1 线程模块2.2 使用 threading 模块创建线程2.3 线程同步2.4 线程优先级队列&#xff08; Queue&#xff09; 3. 日期和时间4. SMTP发送邮件4.1 使用Python发送HTML格式的邮件4.2 Python…

springboot服务注册到Eureka,端口总是默认8080,自己配置端口不生效

这段时间接手了一个公司的老项目&#xff0c;用的是SpringCloud&#xff0c;在我用的时候突然发现有一个服务&#xff0c;注册到Eureka后&#xff0c;界面显示的端口和实际Ribbon调用的实例端口是不一致的&#xff0c;后来我自己写了个端口获取了一下所有的实例信息&#xff0c…

UltralSO软碟通制作Linux系统盘

第一步&#xff1a; 下载镜像 阿里云下载地址&#xff1a;https://mirrors.aliyun.com/centos-vault/ 按照需求选择系统版本&#xff0c;我这要求安装CentOS7.5的系统&#xff0c;我以CentOS7.5为例 第二步&#xff1a; 下载UltralSO软件 官网下载地址&#xff1a;https://cn.…

山西电力市场日前价格预测【2023-08-26】

日前价格预测 预测明日&#xff08;2023-08-26&#xff09;山西电力市场全天平均日前电价为287.61元/MWh。其中&#xff0c;最高日前电价为318.26元/MWh&#xff0c;预计出现在19: 30。最低日前电价为246.18元/MWh&#xff0c;预计出现在05: 15。 价差方向预测 1&#xff1a; 实…

反向传播求变量导数

反向传播求变量导数 1. 相关习题2. 推导流程2.1 相关公式2.3 变量导数求解 3. 代码实现3.1 参数对应3.2 代码实现 以前只知道反向传播通过链式法则实现今天看书发现图片上求出来的值自己算不出来所以自己算了一下&#xff0c;记录一下&#xff0c;并运行了书中的代码相关书籍&a…

Ceph入门到精通-如何编译安装Quagga?

Quagga 1. 理论部分 1.1 软件简介 Quagga中文翻译斑驴&#xff0c;是一种先进的路由软件包&#xff0c;提供一套基于TCP/IP的路由协议。 1.2 斑驴的应用场景 – 使得操作系统变成专业的路由 – 使得操作系统具有与传统路由通过路由协议直接对接 1.3 斑驴支持的路由协议 …

linux篇---使用systemctl start xxx启动自己的程序|开机启动|守护进程

linux篇---使用systemctl start xxx启动自己的程序|开机启动|守护进程 1、创建服务2、修改权限3、启动服务4、测试 机器&#xff1a;Nvidia Jetson Xavier系统&#xff1a;ubuntu 18.04 最近在使用symfony的console组件&#xff0c;需要执行一个后台的php进程&#xff0c;并且…

DevOps之自动化测试

什么是自动化测试&#xff1f; 明确一下自动化测试不是什么。自动化测试不是指自动化生成测试代码&#xff0c;而是自动化地执行由开发人员或测试人员编写的测试代码。正如下面这句谚语&#xff1a;“绝不要手工去做任何可以被自动化处理的事情。——Curt Hibbs” 之前是由人…

Windows 桌面运维及安全管理

什么是桌面运维 桌面运维是IT管理的重要部分&#xff0c;是一种系统管理的技术&#xff0c;它的主要目的是通过管理用户、计算机和其他设备来提高组织的效率。它不仅能够降低维护成本&#xff0c;而且还能够提高系统的可用性。 如今随着企业设备越来越丰富&#xff0c;桌面运…