C++ 网络编程项目fastDFS分布式文件系统(一)

news2024/12/23 9:28:40

目录

1.项目架构图

1.1 一些概念

1.2 项目架构图

2. 分布式文件系统

2.1 传统文件系统

2.2 分布式文件系统

3. FastDFS

3.1 fastDFS介绍

3.2 fastDFS安装

3.3 fastDFS配置文件

3.4 fastDFS的启动

4. fastDFS状态检测

4.1 对file_id的解释

4. 2上传下载代码实现

5. 源码安装 - 回顾


1.项目架构图

1.1 一些概念

  1. 什么是服务器

    • 硬件: 一台配置高的电脑
    • 软件: 电脑必须有一个能够解析http协议的软件
  2. 常见的Web服务器

    • tomcat服务器

      • apache组织产品, 开源的免费服务器
    • weblogic 服务器

      • bea公司, 收费的服务器
      • 不交费, 访问量受限制
    • IIS服务器

      • Internet Information Server

      • 微软公司主推的服务器

    • nginx

      • 小巧且高效的HTTP服务器
      • 也可以做一个高效的负载均衡反向代理
      • 邮件服务器
        • pop3/smtp/imap

1.2 项目架构图

        

1. 客户端

   - 网络架构:

     - b/s

       - 必须使用http协议

     - c/s

       - 协议可以随意选择

       - Qt -> http

2. 服务器

   - Nginx

     - 能处理静态请求 -> html, jpg

     - 动态请求无法处理

     - 服务器集群之后, 每台服务器上部署的内容必须相同

   - fastCGI

     - 帮助服务器处理动态请求

3. 反向代理服务器

   - 客户端并不能直接访问web服务器, 直接访问到的是反向代理服务器

   - 客户端静请求发送给反向代理服务器, 反向代理将客户端请求转发给web服务器

4. 关系型数据库

   - 存储文件属性信息

   - 用户的属性信息

5. redis - 非关系型数据库 (内存数据库)

   - 作用提出程序效率

   - 存储是服务器经常要从关系型数据中读取的数据

6. FASTDFS - 分布式文件系统

   - 存储文件内容

   - 供用户下载

2. 分布式文件系统

2.1 传统文件系统

- 传统的文件系统格式:

  - ntfs / fat32 / ext3 / ext4

- 可以被挂载和卸载

2.2 分布式文件系统

> 文件系统的全部, 不在同一台主机上,而是在很多台主机上,多个分散的文件系统组合在一起,形成了一个完整的文件系统。

分布式文件系统:

 1. 需要有网络

   2. 多台主机

     - 不需要在同一地点

> 3. 需要管理者

> 4. 编写应用层的管理程序

     - 不需要编写

        

3. FastDFS

3.1 fastDFS介绍

1. fastDFS概述

   > - 是用**c语言**编写的一款开源的分布式文件系统。

   >   - 余庆 - 淘宝的架构师

   > - 为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,注重高可用、高性能等指标

   >   - 冗余备份: 纵向扩容

   >   - 线性扩容: 横向扩容

   > - 可以很容易搭建一套高性能的文件服务器集群提供文件==**上传、下载**==等服务。

   >   - 图床

   >   - 网盘

2. fastDFS框架中的三个角色

   - 追踪器 ( Tracker ) - 管理者 - 守护进程

     - 管理存储节点

   - 存储节点 - storage - 守护进程

     - 存储节点是有多个的

   - 客户端 - 不是守护进程, 这是程序猿编写的程序

     - 文件上传

     - 文件下载

3. fastDFS三个角色之间的关系

 

1. 追踪器

      - 最先启动追踪器

   2. 存储节点

      - 第二个启动的角色

      - 存储节点启动之后, 会单独开一个线程

        - 汇报当前存储节点的容量, 和剩余容量

        - 汇报数据的同步情况

        - 汇报数据被下载的次数

   3. 客户端

      - 最后启动

        - 上传

          - 连接追踪器, 询问存储节点的信息

            - 我要上传1G的文件, 询问那个存储节点有足够的容量

            - 追踪器查询, 得到结果

            - 追踪器将查到的存储节点的IP+端口发送给客户端

            - 通过得到IP和端口连接存储节点

            - 将文件内容发送给存储节点

        - 下载

          - 连接追踪器, 询问存储节点的信息

            - 问一下, 要下载的文件在哪一个存储节点

            - 追踪器查询, 得到结果

            - 追踪器将查到的存储节点的IP+端口发送给客户端

            - 通过得到IP和端口连接存储节点

            - 下载文件

4. fastDFS集群

1. 追踪器集群

  •       - 为什么集群?
  •         - 避免单点故障
  •       - 多个Tracker如何工作?
  •         - 轮询工作
  •       - 如何实现集群?
  •         - 修改配置文件

   2. 存储节点集群

  •       - fastDFS管理存储节点的方式?
  •         - 通过分组的方式完成的
  •       - 集群方式(扩容方式)
  •         - 横向扩容 - 增加容量
  •           - 添加一台新的主机 -> 容量增加了
  •           - 假设当前有两个组: group1, group2
  •             - 需要添加一个新的分组 -> group3
  •               - 新主机属于第三组
  •           - 不同组的主机之间不需要通信
  •         - 纵向扩容 - 数据备份
  •           - 假设当前有两个组: group1, group2
  •             - 将新的主机放到现有的组中
  •             - 每个组的主机数量从1 -> N
  •               - 这n台主机的关系就是相互备份的关系
  •               - 同一个组中的主机需要通信
  •               - 每组的容量 == 容量最小的这台主机
  •       - 如何实现?
  •         - 通过修改配置文件可实现

3.2 fastDFS安装

1. fastDFS安装

   - libfastcommon-1.36.zip

     - fastdfs的基础库包

     - unzip xxx.zip

     - ./make.sh

     - ./make.sh install

   - fastdfs-5.10.tar.gz

     - tar zxvf xxx.tar.gz(x表示解压缩,z 表示gz)

     - ./make.sh

     - ./make.sh install

2. 测试

   ```shell

   #fastDFS安装的所有的可执行程序:

   /usr/bin/fdfs_*

   fdfs_test

   ```

        进入到root用户 sudo su-   退出exit

1. fastDFS安装

   - libfastcommon-1.36.zip

     - fastdfs的基础库包

     - unzip xxx.zip

     - ./make.sh

     - ./make.sh install

   - fastdfs-5.10.tar.gz

     - tar zxvf xxx.tar.gz(x表示解压缩,z 表示gz)

     - ./make.sh

     - ./make.sh install

2. 测试

   ```shell

   #fastDFS安装的所有的可执行程序:

   ls /usr/bin/fdfs_*

   (ls fdfs_* 是一个命令,用于列出以 "fdfs_" 开头的文件或目录)

   fdfs_test

   ```

 

3.3 fastDFS配置文件

配置文件默认位置:  /etc/fdfs

> client.conf.sample  storage.conf.sample  storage_ids.conf.sample  tracker.conf.sample

1. tracker 配置文件

   ```shell

   # 将追踪器和部署的主机的IP地址进程绑定, 也可以不指定

   # 如果不指定, 会自动绑定当前主机IP, 如果是云服务器建议不要写(ctrl+shift +v )在vi 编辑器里面复制内容

   bind_addr=192.168.247.135

   # 追踪器监听的端口

   port=22122

   # 追踪器存储日志信息的目录, xxx.pid文件, 必须是一个存在的目录

   base_path=/home/yuqing/fastdfs

   ```

2. storage 配置文件

   ```shell

   # 当前存储节点对应的主机属于哪一个组

   group_name=group1

   # 当前存储节点和所应该的主机进行IP地址的绑定, 如果不写, 有fastdfs自动绑定

   bind_addr=

   # 存储节点绑定的端口

   port=23000

   # 存储节点写log日志的路径

   base_path=/home/yuqing/fastdfs

   # 存储节点提供的存储文件的路径个数

   store_path_count=2

   # 具体的存储路径

   store_path0=/home/yuqing/fastdfs

   store_path1=/home/yuqing/fastdfs1

   # 追踪器的地址信息

   tracker_server=192.168.247.135:22122

   tracker_server=192.168.247.136:22122

   ```

3. 客户端配置文件

   ```shell

   # 客户端写log日志的目录

   # 该路径必须存在

   # 当前的用户对于该路径中的文件有读写权限

   # 当前用户robin

   # 指定的路径属于root

   base_path=/home/yuqing/fastdfs

   # 要连接的追踪器的地址信息

   tracker_server=192.168.247.135:22122

   tracker_server=192.168.247.136:22122

   ```

vi 编辑器撤销的快捷键

在Vi编辑器中,撤销操作通常由以下快捷键完成:

  1. 撤销上一步操作:

    • 在普通模式下,按下 u 键。
  2. 撤销多个操作:

    • 在普通模式下,输入数字加上 u 键。例如,要撤销最近的两个操作,可以按下 2u

请注意,撤销操作是逐步递减的,意味着每次按下 u 键,都会撤销先前的操作,直到没有更多的操作可撤销为止。

如果你误操作或者需要撤销更多的步骤,你可以重复按下 u 键来连续执行撤销操作。记住,这些操作都是在普通模式下完成的。

总结:

  • 进入普通模式。
  • 按下 u 键来逐步撤销操作。
  • 按下数字加上 u 键来撤销多个操作。

这些快捷键可帮助你在Vi编辑器中轻松地进行撤销操作。

vi 编辑器反撤销的快捷键

在Vi编辑器中,反撤销操作通常由以下快捷键完成:

  1. 反撤销上一步操作:
    • 在普通模式下,按下 Ctrl + r 键。

与撤销操作类似,反撤销操作也是逐步递减的。每次按下 Ctrl + r 键,都会重新应用之前被撤销的操作,直到没有更多的操作可反撤销为止。

请注意,反撤销操作是在普通模式下完成的。

总结:

  • 进入普通模式。
  • 按下 Ctrl + r 键来逐步进行反撤销操作。

这些快捷键可以帮助你在Vi编辑器中进行反撤销操作,恢复之前被撤销的更改

3.4 fastDFS的启动

1. 第一个启动追踪器  - 守护进程

   ```shell

   # 启动程序在 /usr/bin/fdfs_*

   # 启动

   fdfs_trackerd 追踪器的配置文件(/etc/fdfs/tracker.conf)

   # 关闭

   fdfs_trackerd 追踪器的配置文件(/etc/fdfs/tracker.conf)  stop

   # 重启

   fdfs_trackerd 追踪器的配置文件(/etc/fdfs/tracker.conf) restart

   ```

查看

ps aux|grep fdfs_*

2. 第二个启动存储节点 - 守护进程

   ```shell

   # 启动

   fdfs_storaged 存储节点的配置文件(/etc/fdfs/stroga.conf)

   # 关闭

   fdfs_storaged 存储节点的配置文件(/etc/fdfs/stroga.conf) stop

   # 重启

   fdfs_storaged 存储节点的配置文件(/etc/fdfs/stroga.conf) restart

   ```

3. 最后启动客户端 - 普通进程

   ```shell

   # 上传

   fdfs_upload_file 客户端的配置文件(/etc/fdfs/client.conf) 要上传的文件

 

   # 得到的结果字符串: group1/M00/00/00/wKj3h1vC-PuAJ09iAAAHT1YnUNE31352.c

   # 下载

   fdfs_download_file 客户端的配置文件(/etc/fdfs/client.conf) 上传成功之后得到的字符串(fileID)

   ```

4. fastDFS状态检测

    - 命令

      ```shell

      fdfs_monitor /etc/fdfs/client.conf

      ```

    - [Storage Server的7种状态](https://blog.csdn.net/u014723529/article/details/46048411)

      https://blog.csdn.net/u014723529/article/details/46048411

      ```shell

      # FDFS_STORAGE_STATUS:INIT      :初始化,尚未得到同步已有数据的源服务器

      # FDFS_STORAGE_STATUS:WAIT_SYNC :等待同步,已得到同步已有数据的源服务器

      # FDFS_STORAGE_STATUS:SYNCING   :同步中

      # FDFS_STORAGE_STATUS:DELETED   :已删除,该服务器从本组中摘除

      # FDFS_STORAGE_STATUS:OFFLINE   :离线

      # FDFS_STORAGE_STATUS:ONLINE    :在线,尚不能提供服务

      # FDFS_STORAGE_STATUS:ACTIVE    :在线,可以提供服务

      ```

4.1 对file_id的解释

        

  • - group1

  - 文件上传到了存储节点的哪一个组

  - 如果有多个组这个组名可变的

  • - M00 - 虚拟目录

  - 和存储节点的配置项有映射

    - store_path0=/home/yuqing/fastdfs/data    ->  M00

      store_path1=/home/yuqing/fastdfs1/data   -> M01

  • - 00/00

  - 实际的路径

  - 可变的

- wKhS_VlrEfOAdIZyAAAJTOwCGr43848.md

  - 文件名包含的信息

  - 采用Base64编码

    - 包含的字段包括

      - 源storage server Ip 地址  

      - 文件创建时间  

      - 文件大小  

      - 文件CRC32效验码

        - 循环冗余校验  

      - 随机数

4. 2上传下载代码实现

1. 使用多进程方式实现

   - exec函数族函数

     - execl

     - execlp

  •    - 父进程

     - 子进程 -> 执行

       execlp("fdfs_upload_file" , "xx", arg, NULL), 有结果输出, 输出到终端

       - 不让它写到终端 -> 重定向dup2(old, new)

         - old-> 标准输出

         - new -> 管道的写端

         - 文件描述符

         - 数据块读到内存 -> 子进程

                   - 数据最终要给到父进程

       - pipe -> 读端, 写端

         - 在子进程创建之前创建就行了

  •      - 父进程

       - 读管道 -> 内存

       - 内存数据写数据库

2. 使用fastDFS API实现

fdfs_upload_file.c

/**
* Copyright (C) 2008 Happy Fish / YuQing
*
* FastDFS may be copied only under the terms of the GNU General
* Public License V3, which may be found in the FastDFS source kit.
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
**/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "fdfs_client.h"
// #include "logger.h"


//加上const 明显就知道了这是一个传入的文件  fileID 是一个传出n参数
int upload_file(const char *confFile,const char *myFile,char *fileID)
{
	char group_name[FDFS_GROUP_NAME_MAX_LEN + 1];
	ConnectionInfo *pTrackerServer;
	int result;
	int store_path_index;
	ConnectionInfo storageServer;
	
	
	 
	if ((result=fdfs_client_init(confFile)) != 0)
	{
		return result;
	}
	pTrackerServer = tracker_get_connection();
	if (pTrackerServer == NULL)
	{
		fdfs_client_destroy();
		return errno != 0 ? errno : ECONNREFUSED;
	}

	*group_name = '\0';
	 
 	 if ((result=tracker_query_storage_store(pTrackerServer, \
	                &storageServer, group_name, &store_path_index)) != 0)
	{
		fdfs_client_destroy();
		fprintf(stderr, "tracker_query_storage fail, " \
			"error no: %d, error info: %s\n", \
			result, STRERROR(result));
		return result;
	}

	result = storage_upload_by_filename1(pTrackerServer, \
			&storageServer, store_path_index, \
			myFile, NULL, \
			NULL, 0, group_name, fileID);
	if (result == 0)
	{
		printf("%s\n", fileID);
	}
	else
	{
		fprintf(stderr, "upload file fail, " \
			"error no: %d, error info: %s\n", \
			result, STRERROR(result));
	}

	tracker_disconnect_server_ex(pTrackerServer, true);
	fdfs_client_destroy();

	return result;
}



//size fileID 对长度的描述 
int upload_file1(const char *confFlie,const char *uploadFile,char *fileID,int size)
{

     //创建管道
     int fd[2];
   int ret=pipe(fd);

   if(ret==-1){
     perror("pipe error");
     exit(0);
   }

   //创建子进程
   pid_t pid=fork();


     //子进程
   if(pid==0){

     //将new 重定向号old  将标准输出重定向到管道写的一段
     dup2(fd[1],STDOUT_FILENO);

     //关闭u读的a一顿
     close(fd[0]);

     execlp("fdfs_upload_file","xxx",confFlie,uploadFile,NULL);
     perror("execlp error");

   }

   else{

     //父进程 读ao管道 关闭写端

     close(fd[1]);

     char buf[1024];
     read(fd[0],fileID,size);
     
     //回收pid
     wait(NULL);





   }


  return 0;
}

main.c

#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include <sys/types.h>
#include<string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include<ctype.h>
#include<sys/stat.h>
#include "fdfs_upload_file.h"


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

     char fileID[1024]={0};
     upload_file("/etc/fdfs/client.conf","main.c",fileID);
     printf("fileId:%s\n",fileID);
     printf("=================================\n");
     upload_file1("/etc/fdfs/client.conf","main.c",fileID,sizeof(fileID));
     printf("fileID: %s\n",fileID);

  return 0;
}

fdfs_upload._file.h 


#ifndef _FDFS_UPLOAD_FILE_H
#define _FDFS_UPLOAD_FILEH
int upload_file(const char *confFile,const char *myFile,char *fileID);
int upload_file1(const char *confFlie,const char *uploadFile,char *fileID,int size);
#endif
/*
#ifndef 和 #endif 都是C/C++预处理指令,用于条件编译,起到防止文件被重复包含的作用。
具体作用如下:
#ifndef:
该预处理指令的作用是检查给定的宏是否已经定义。
如果给定的宏尚未定义,那么条件成立,编译器会开始处理下面的代码。
如果给定的宏已经定义,那么条件不成立,编译器会跳过下面的代码。
#ifndef 的格式是 #ifndef MACRO_NAME,其中 MACRO_NAME 是一个宏的名字。
#define:
这是一个定义预处理宏的指令。
当 #ifndef 条件成立时,#define 指令会定义一个宏。
#define 的格式是 #define MACRO_NAME。
#endif:
这是一个结束预处理条件的指令。
当条件为真时,#endif 表示条件编译块的结束。
#endif 用于匹配 #ifndef 的开始。
综上所述,#ifndef 和 #endif 配合使用,可以防止头文件被重复包含。
如果头文件已经被包含过,那么下次再次遇到 #ifndef 条件不成立,代码块将被跳过,
避免了重复定义和编译错误。
*/


如果没有找到头文件的话,还报错说明没有添加动态库(.so 文件,或者静态库.a 文件)
编译gcc *.c -I 连接外部的库,外部库的寻找

寻找.h 的i路径
find / -name "fdfs_client.h" 

//最后是链接的动态库的部分 
gcc *.c -I /usr/include/fastdfs/ -I /usr/include/fastcommon -lfdfsclient


 

     

5. 源码安装 - 回顾

安装流程:

1. 以下文件, 里边有安装步骤

   - readme

   - readme.md

   - INSTALL

2. 找 可执行文件 <font color=green>configure</font>

   - 执行这个可执行文件

     - 检测安装环境

     - 生成 makefile

3. 执行**make**命令

   - 编译源代码

     - 生成了动态库

     - 静态库

     - 可执行程序

4. 安装 **make install** (需要管理员权限)

   - 将第三步生成的动态库/动态库/可执行程序拷贝到对应的系统目录

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

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

相关文章

电流的测量(分流电流表)

在当今的大多数仪器应用中&#xff0c;可以使用两种常见的电流测量方法&#xff1a;分流电流表方法和反馈电流表方法。分流电流表方法通常与通用数字万用表 (DMM)一起使用&#xff0c;用于测量分流电阻器上的电压测量值。该电压测量结果与已知的电阻值相结合&#xff0c;得出电…

ADM2587E在RS485和RS422接口的应用(ADM2587E电路原理图和程序开发)

最近做一个项目使用到ADM2587E&#xff0c;为了解决公司历史遗留的问题&#xff08;ADM2587E芯片发烫&#xff0c;容易烧毁&#xff0c;485设备只能手拉手连接三四个&#xff0c;就通信不正常现象&#xff09;&#xff0c;认真阅读了Datasheet和官网LayOut的一些设计文档&#…

4、基于mysql实现分布式锁

目录 4.1. 基本思路4.2. 代码实现4.3 缺陷及解决方案 4.1. 基本思路 synchronized关键字和ReetrantLock锁都是独占排他锁&#xff0c;即多个线程争抢一个资源时&#xff0c;同一时刻只有一个线程可以抢占该资源&#xff0c;其他线程只能阻塞等待&#xff0c;直到占有资源的线程…

棒球和垒球的区别·棒球联盟

棒球和垒球的区别 1. 定义和起源 棒球起源于19世纪中叶的美国&#xff0c;最初被认为是一种游戏&#xff0c;而并非体育运动。那时&#xff0c;棒球常常被孩子们用来进行休闲娱乐。在20世纪初&#xff0c;它才开始被纳入体育运动的范畴。 垒球则是棒球的近亲&#xff0c;同样…

Java | 字符串

目录 一、String类 1.1 声明字符串 1.2 创建字符串 二、连接字符串 2.1 连接多个字符串 2.2 连接其他数据类型 三、获取字符串信息 3.1 获取字符串长度 3.2 字符串查找 3.3 获取指定索引位置的字符 四、字符串操作 4.1 获取字符串 4.2 去除空格 4.3 字符串替换 …

Android JNI开发从0到1,java调C,C调Java,保姆级教程详解

前些天发现了一个蛮有意思的人工智能学习网站,8个字形容一下"通俗易懂&#xff0c;风趣幽默"&#xff0c;感觉非常有意思,忍不住分享一下给大家。 &#x1f449;点击跳转到教程 第一步首先配置Android studio的NDK开发环境&#xff0c;首先在Android studio中下载NDK…

QT-基于Buildroot构建系统镜像下实现QT开发

QT-基于Buildroot构建系统镜像下实现QT开发 BuildRootUboot的仓库地址和commit idKernel 的仓库地址和commit id BuildRoot已编译库在Windows上的Create上创建项目编译QT项目 BuildRoot 这部分按照100ask官网的教程走即可: Uboot的仓库地址和commit id https://e.coding.net/…

如何使用ChatGPT设计LOGO,只需知道品牌名字就能完成傻瓜式操作

​独特且引人注目的LOGO对于引导用户/消费者快速识别并与你建立联系至关重要。然而&#xff0c;聘请专业的设计师来创建个性化LOGO可能非常昂贵。这里可以使用使用ChatGPT。[1] 你只需要&#xff1a; 准备好公司名称&#xff1b; 能用ChatGPT&#xff0c;用来给BingChat喂log…

WebAPIs 第一天

1.声明变量const优先&#xff08;补充&#xff09; 2.WebAPI基本认知 作用和分类 DOM树和DOM对象 3.获取DOM元素 4.DOM修改元素内容 5.操作元素属性 6.定时器-间歇函数 一.声明变量const优先 ① 变量声明有var let const ② 建议const优先&#xff0c;尽量使用const…

【枚举+推式子】牛客小白月赛 63 E

登录—专业IT笔试面试备考平台_牛客网 题意&#xff1a; 思路&#xff1a; 首先是个计数问题&#xff0c;考虑组合数学 组合数学就是在考虑枚举所有包含1和n的区间 这个典中典就是枚举1和n的位置然后算贡献 双指针超时&#xff0c;考虑推式子&#xff1a; Code&#xff1a…

分类过程中的一种遮挡现象

( A, B )---3*30*2---( 1, 0 )( 0, 1 ) 让网络的输入只有3个节点&#xff0c;AB训练集各由6张二值化的图片组成&#xff0c;让A&#xff0c;B中各有3个点&#xff0c;且不重合&#xff0c;统计迭代次数并排序。 其中有10组数据 差值结构 迭代次数 构造平均列A 构造平均列AB…

C++笔记之函数参数列表中设置默认值

C笔记之函数参数列表中设置默认值 code review! 代码 #include <iostream>// 函数声明时设置默认值 void printInfo(std::string name "Unknown", int age 0);int main() {printInfo(); // 使用默认参数值printInfo("Alice", 25);…

Scractch3.0_Arduino_ESP32_学习随记_蓝牙鼠标(四)

蓝牙鼠标 目的器材程序联系我们 目的 通过C02实现蓝牙鼠标。 器材 硬件: 齐护机器人C02 购买地址 软件:scratch3.0 下载地址:官网下载 程序 蓝牙鼠标使用使用ESP32自带的BLE蓝牙&#xff0c;不需要再外接模块。可以实现鼠标移动&#xff0c;左右键的点击动作。 联系我们…

C++进阶 智能指针

本篇博客简介&#xff1a;介绍C中的智能指针 智能指针 为什么会存在智能指针内存泄露内存泄漏定义内存泄漏的危害如何检测内存泄漏如何避免内存泄漏 智能指针的使用及其原理RAII设计一个智能指针C官方的智能指针 定制删除器智能指针总结 为什么会存在智能指针 我们首先来看下面…

消息队列(11) - 通信协议的设计

目录 通信协议设计代码实现 通信协议设计 对于我们客户端与服务器之间的通信协议我们约定如下&#xff1a; 具体的协议设计: 之后我们传递的参数也是这些 关于 type其实是在描述当前这个请求 、 响应是在调用那个API 约定如下 对于channel ,是tcp链接中的一个逻辑上的链接,…

Python实现图片文本支持中文,自定义字体

Python实现图片文本支持中文&#xff0c;自定义字体 # 支持中文 import matplotlib #用下载好的字体文件设置字体&#xff0c;从而正确显示中文 myfont matplotlib.font_manager.FontProperties(fnamer"./simsun.ttc") # 自定义的字体文件 plt.figure(figsize (1…

udp一般不会存在错数据

UDP在传输过程中会出现丢包的情况&#xff0c;但不会导致数据错乱的情况&#xff0c;这涉及到UDP协议的特性和工作原理。 无连接性&#xff1a;UDP是一种无连接的传输协议&#xff0c;每个UDP数据包都是独立的&#xff0c;没有依赖关系。因此&#xff0c;即使发生数据包丢失&am…

Golang 局部变量、全局变量 声明

文章目录 一、局部变量二、全局变量 一、局部变量 四种声明方式 多变量声明&#xff1a; package mainimport "fmt"//局部变量声明 func main() {//方法一: 声明一个变量和数据类型&#xff0c;不初始化值&#xff1b;默认值为0&#xff1b;var lvA intfmt.Printl…

圆圈中最后剩下的数字——剑指 Offer 62

文章目录 题目描述解法一题目描述 解法一 class Solution

Spring MVC静态资源映射

Spring MVC静态资源映射 静态资源映射。使用容器的默认Servletlocationmapping&#xff1a;cache-periodorder Spring MVC需要对RESTful风格的URL提供支持&#xff0c;而真正的RESTful风格的URL不应该带有任何后缀&#xff0c;因此将Spring MVC拦截的URL改为“/”&#xff08;正…