C# 网络协议第三方库Protobuf的使用
为什么要使用二进制数据
通常我们写一个简单的网络通讯软件可能使用的最多的是字符串类型,比较简单,例如发送格式为(head)19|Msg:Heart|100,x,y,z…,在接收端会解析收到的socket数据。
这样通常是完全可行的,但是随着数据量变大,网络吞吐量就变大,可能发送的字符串就不合适了,可能会数据量变大。
举例:
假如你要发送你的年收入和你的坐标,例如你的年收入是一亿两千万(123,456,789)(幸福死了)你的坐标是1.234567,如果通过字符串传输,你的收入就是9位,你的坐标可能你发小数因为精度问题还不准确
通常使用二进制发送会大大节省。一个int32是4位,float类型也是4位,这样8位就够了。
看下面的例子:
static void Main(string[] args){Console.WriteLine("Hello, World!");int money = 123456789;float x = 1.234567f;byte[] moneybyte = BitConverter.GetBytes(money);byte[] xbyte = BitConverter.GetBytes(x);Console.WriteLine($"moneybyte: {moneybyte.Length},{money} :xbyte: {xbyte.Length},{x}" );int moneyget = BitConverter.ToInt32(moneybyte);float xget = BitConverter.ToSingle(xbyte);Console.WriteLine($"moneyget: {moneyget} :xget: {xget}");}
输出结果
Hello, World!
moneybyte: 4,123456789 :xbyte: 4,1.234567
moneyget: 123456789 :xget: 1.234567
我们看到对于数字32位占4个字节,这样如果是大量的数据就会很节省,甚至你可以使用int16,或者bool占用更小的字节。
对于大量密集的网络程序使用二进制数据进行发送很必要的。
初步思考如何方便的使用二进制或者封装
是不是有这样的疑问,如果要同步一个数据包含很多类型数据,如何拼接和解析呢,好像二进制没有字符串那么直观和好使用。
比如我要同步的数据是如下数据(通常我们把这种格式称作协议),需要发送结构和解析正确的匹配才能解析。
协议头|发送的大小|我的名字|18|123456789|1.234567|我的介绍|结束
对于二进制如果我们有这样的结构
public struct mydata
{public string name;public int age;public int money;public float x;public string readme;}
我们可以根据结构体内的属性进行二进制发送就可以了,接收方也有这样的数据结构也进行解析就可以了,这里要注意每个属性的顺序不能是错误的。
网上有一些把结构体或者类打包成二进制的方法,这里就不过多说明了。
使用Protobuf
protobuf就是专门为实现这个而生的,从名字就可以看出来。
Protobuf 的官方 C# 库是 Google.Protobuf,可以通过 NuGet 包管理器来方便的使用。
我们这里就来简单说一下如何使用:
安装
首先vs里创建一个c#控制台程序。
然后可以通过 NuGet安装
第一个协议
我们创建一个Person.proto文件
syntax = "proto3";message Person {string name = 1;int32 age = 2;string email = 3;
}
我们需要把这个proto转成c#可以解析的c#程序
我们可以来到Protobuf库下载执行程序,这个程序可以把proto解析成c#文件。
我们下载好之后:输入指令
F:\Downloads\protoc-29.3-win64\bin>protoc --csharp_out=. Person.protoF:\Downloads\protoc-29.3-win64\bin>
具体指令可以参考库里的文档
–csharp_out输出cs文件 .是当前路径
执行成功后会有一个Person.cs我们可以放入我们的项目,这样就很容易解析协议了。
生成的cs如下:
// <auto-generated>
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: test.proto
// </auto-generated>
#pragma warning disable 1591, 0612, 3021, 8981
#region Designer generated codeusing pb = global::Google.Protobuf;
using pbc = global::Google.Protobuf.Collections;
using pbr = global::Google.Protobuf.Reflection;
using scg = global::System.Collections.Generic;
/// <summary>Holder for reflection information generated from test.proto</summary>
public static partial class TestReflection {#region Descriptor/// <summary>File descriptor for test.proto</summary>public static pbr::FileDescriptor Descriptor {get { return descriptor; }}private static pbr::FileDescriptor descriptor;static TestReflection() {byte[] descriptorData = global::System.Convert.FromBase64String(string.Concat("Cgp0ZXN0LnByb3RvIjIKBlBlcnNvbhIMCgRuYW1lGAEgASgJEgsKA2FnZRgC","IAEoBRINCgVlbWFpbBgDIAEoCWIGcHJvdG8z"));descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,new pbr::FileDescriptor[] { },new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {new pbr::GeneratedClrTypeInfo(typeof(global::Person), global::Person.Parser, new[]{ "Name", "Age", "Email" }, null, null, null, null)}));}#endregion}
#region Messages
[global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")]
public sealed partial class Person : pb::IMessage<Person>
#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE, pb::IBufferMessage
#endif
{private static readonly pb::MessageParser<Person> _parser = new pb::MessageParser<Person>(() => new Person());private pb::UnknownFieldSet _unknownFields;[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public static pb::MessageParser<Person> Parser { get { return _parser; } }[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public static pbr::MessageDescriptor Descriptor {get { return global::TestReflection.Descriptor.MessageTypes[0]; }}[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]pbr::MessageDescriptor pb::IMessage.Descriptor {get { return Descriptor; }}[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public Person() {OnConstruction();}partial void OnConstruction();[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public Person(Person other) : this() {name_ = other.name_;age_ = other.age_;email_ = other.email_;_unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);}[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public Person Clone() {return new Person(this);}/// <summary>Field number for the "name" field.</summary>public const int NameFieldNumber = 1;private string name_ = "";[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public string Name {get { return name_; }set {name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");}}/// <summary>Field number for the "age" field.</summary>public const int AgeFieldNumber = 2;private int age_;[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public int Age {get { return age_; }set {age_ = value;}}/// <summary>Field number for the "email" field.</summary>public const int EmailFieldNumber = 3;private string email_ = "";[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public string Email {get { return email_; }set {email_ = pb::ProtoPreconditions.CheckNotNull(value, "value");}}[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public override bool Equals(object other) {return Equals(other as Person);}[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public bool Equals(Person other) {if (ReferenceEquals(other, null)) {return false;}if (ReferenceEquals(other, this)) {return true;}if (Name != other.Name) return false;if (Age != other.Age) return false;if (Email != other.Email) return false;return Equals(_unknownFields, other._unknownFields);}[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public override int GetHashCode() {int hash = 1;if (Name.Length != 0) hash ^= Name.GetHashCode();if (Age != 0) hash ^= Age.GetHashCode();if (Email.Length != 0) hash ^= Email.GetHashCode();if (_unknownFields != null) {hash ^= _unknownFields.GetHashCode();}return hash;}[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public override string ToString() {return pb::JsonFormatter.ToDiagnosticString(this);}[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public void WriteTo(pb::CodedOutputStream output) {#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODEoutput.WriteRawMessage(this);#elseif (Name.Length != 0) {output.WriteRawTag(10);output.WriteString(Name);}if (Age != 0) {output.WriteRawTag(16);output.WriteInt32(Age);}if (Email.Length != 0) {output.WriteRawTag(26);output.WriteString(Email);}if (_unknownFields != null) {_unknownFields.WriteTo(output);}#endif}#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {if (Name.Length != 0) {output.WriteRawTag(10);output.WriteString(Name);}if (Age != 0) {output.WriteRawTag(16);output.WriteInt32(Age);}if (Email.Length != 0) {output.WriteRawTag(26);output.WriteString(Email);}if (_unknownFields != null) {_unknownFields.WriteTo(ref output);}}#endif[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public int CalculateSize() {int size = 0;if (Name.Length != 0) {size += 1 + pb::CodedOutputStream.ComputeStringSize(Name);}if (Age != 0) {size += 1 + pb::CodedOutputStream.ComputeInt32Size(Age);}if (Email.Length != 0) {size += 1 + pb::CodedOutputStream.ComputeStringSize(Email);}if (_unknownFields != null) {size += _unknownFields.CalculateSize();}return size;}[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public void MergeFrom(Person other) {if (other == null) {return;}if (other.Name.Length != 0) {Name = other.Name;}if (other.Age != 0) {Age = other.Age;}if (other.Email.Length != 0) {Email = other.Email;}_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);}[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public void MergeFrom(pb::CodedInputStream input) {#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODEinput.ReadRawMessage(this);#elseuint tag;while ((tag = input.ReadTag()) != 0) {if ((tag & 7) == 4) {// Abort on any end group tag.return;}switch(tag) {default:_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);break;case 10: {Name = input.ReadString();break;}case 16: {Age = input.ReadInt32();break;}case 26: {Email = input.ReadString();break;}}}#endif}#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {uint tag;while ((tag = input.ReadTag()) != 0) {if ((tag & 7) == 4) {// Abort on any end group tag.return;}switch(tag) {default:_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);break;case 10: {Name = input.ReadString();break;}case 16: {Age = input.ReadInt32();break;}case 26: {Email = input.ReadString();break;}}}}#endif}#endregion#endregion Designer generated code
使用
我们开始使用
static void Main(string[] args)
{Console.WriteLine("Hello, World!");var person = new Person{Age = 18,Name = "",Email = ""};byte[] serializedData = person.ToByteArray();Console.WriteLine($"serialsize {serializedData.Length} :Serialized Data: " + BitConverter.ToString(serializedData));// 从字节数组反序列化var deserializedPerson = Person.Parser.ParseFrom(serializedData);Console.WriteLine($"Deserialized Person: Name={deserializedPerson.Name}, Age={deserializedPerson.Age}, Email={deserializedPerson.Email}");}
代码中我们给person赋值,并通过ToByteArray二进制转化,得到二进制数组后就可以通过网络发送了。
当接收方收到这个二进制数据就可以通过ParseFrom进行解析。
执行结果
Hello, World!
serialsize 2 :Serialized Data: 10-12
Deserialized Person: Name=, Age=18, Email=
我们看到二进制大小是2是因为使用了一种变长编码 (varint) 的优化方案可以看下官方的文档。通常短数据比较多,使用变长编码的方式能够节省一些。
到这里就结束了。以上就是Protobuf的简单使用。
参考
https://github.com/protocolbuffers/protobuf
相关文章:
C# 网络协议第三方库Protobuf的使用
为什么要使用二进制数据 通常我们写一个简单的网络通讯软件可能使用的最多的是字符串类型,比较简单,例如发送格式为(head)19|Msg:Heart|100,x,y,z…,在接收端会解析收到的socket数据。 这样通常是完全可行的,但是随着数据量变大&…...
「2024 博客之星」自研Java框架 Sunrays-Framework 使用教程
文章目录 0.序言我的成长历程遇到挫折,陷入低谷重拾信心,迎接未来开源与分享我为何如此看重这次评选最后的心声 1.概述1.主要功能2.相关链接 2.系统要求构建工具框架和语言数据库与缓存消息队列与对象存储 3.快速入门0.配置Maven中央仓库1.打开settings.…...
【Elasticsearch】Springboot编写Elasticsearch的RestAPI
RestAPI 初始化RestClient创建索引库Mapping映射 判断索引库是否存在删除索引库总结 ES官方提供了各种不同语言的客户端,用来操作ES。这些客户端的本质就是组装DSL语句,通过http请求发送给ES。 官方文档地址 由于ES目前最新版本是8.8,提供了全…...
Swift语言的学习路线
Swift语言的学习路线 引言 在现代程序开发中,Swift语言逐渐成为了移动应用程序开发的重要工具,尤其是在iOS和macOS平台上。自2014年发布以来,Swift以其易读性和强大的功能,受到越来越多开发者的青睐。对于初学者而言,…...
63,【3】buuctf web Upload-Labs-Linux 1
进入靶场 点击pass1 查看提示 既然是上传文件,先构造一句话木马,便于用蚁剑连接 <?php eval($_POST[123])?> 将这两处的检查函数删掉 再上传木马 文件后缀写为.php 右键复制图片地址 打开蚁剑连接 先点击测试连接,显示成功后&…...
Leetcode:2239
1,题目 2,思路 循环遍历满足条件就记录,最后返回结果值 3,代码 public class Leetcode2239 {public static void main(String[] args) {System.out.println(new Solution2239().findClosestNumber(new int[]{-4, -2, 1, 4, 8})…...
自然语言处理与NLTK环境配置
自然语言处理(Natural Language Processing, NLP)是人工智能的重要分支,专注于计算机如何理解、分析和生成自然语言。自然语言是人类用于交流的语言,如中文、英文等,这使得自然语言处理成为沟通人与计算机的桥梁。近年来,NLP在诸多领域得到广泛应用,包括文本分析、语言翻…...
分布式搜索引擎02
1. DSL查询文档 elasticsearch的查询依然是基于JSON风格的DSL来实现的。 1.1. DSL查询分类 Elasticsearch提供了基于JSON的DSL(Domain Specific Language)来定义查询。常见的查询类型包括: 查询所有:查询出所有数据,…...
使用 Logback 的最佳实践:`logback.xml` 与 `logback-spring.xml` 的区别与用法
在开发 Spring Boot 项目时,日志是调试和监控的重要工具。Spring Boot 默认支持 Logback 作为日志系统,并提供了 logback.xml 和 logback-spring.xml 两种配置方式。这篇文章将详细介绍这两者的区别、各自的优缺点以及最佳实践。 目录 一、什么是 Logbac…...
【爬虫开发】爬虫开发从0到1全知识教程第12篇:scrapy爬虫框架,介绍【附代码文档】
本教程的知识点为:爬虫概要 爬虫基础 爬虫概述 知识点: 1. 爬虫的概念 requests模块 requests模块 知识点: 1. requests模块介绍 1.1 requests模块的作用: 数据提取概要 数据提取概述 知识点 1. 响应内容的分类 知识点:…...
鸿蒙UI(ArkUI-方舟UI框架)-开发布局
文章目录 开发布局1、布局概述1)布局结构2)布局元素组成3)如何选择布局4)布局位置5)对子元素的约束 2、构建布局1)线性布局 (Row/Column)概述布局子元素在排列方向上的间距布局子元素在交叉轴上的对齐方式(…...
代码随想录_字符串
字符串 344.反转字符串 344. 反转字符串 编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。 不要给另外的数组分配额外的空间,你必须**原地修改输入数组**、使用 O(1) 的额外空间解决这一问题。 思路: 双指针 代…...
2025年1月17日(点亮三色LED)
系统信息: Raspberry Pi Zero 2W 系统版本: 2024-10-22-raspios-bullseye-armhf Python 版本:Python 3.9.2 已安装 pip3 支持拍摄 1080p 30 (1092*1080), 720p 60 (1280*720), 60/90 (640*480) 已安装 vim 已安装 git 学习目标:…...
Spring Boot自动配置原理:如何实现零配置启动
引言 在现代软件开发中,Spring 框架已经成为 Java 开发领域不可或缺的一部分。而 Spring Boot 的出现,更是为 Spring 应用的开发带来了革命性的变化。Spring Boot 的核心优势之一就是它的“自动配置”能力,它极大地简化了 Spring 应用的配置…...
React技术栈搭配(全栈)(MERN栈、PERN栈)
文章目录 1. MERN 栈2. PERN 栈3. React Next.js Node.js4. JAMstack (JavaScript, APIs, Markup)5. React GraphQL Node.js6. React Native Node.js结论 React作为前端框架已经成为了现代web开发的重要组成部分。在全栈开发中,React通常…...
Linux - 线程池
线程池 什么是池? 池化技术的核心就是"提前准备并重复利用资源". 减少资源创建和销毁的成本. 那么线程池就是提前准备好一些线程, 当有任务来临时, 就可以直接交给这些线程运行, 当线程完成这些任务后, 并不会被销毁, 而是继续等待任务. 那么这些线程在程序运行过程…...
以Python构建ONE FACE管理界面:从基础至进阶的实战探索
一、引言 1.1 研究背景与意义 在人工智能技术蓬勃发展的当下,面部识别技术凭借其独特优势,于安防、金融、智能终端等众多领域广泛应用。在安防领域,可助力监控系统精准识别潜在威胁人员,提升公共安全保障水平;金融行业中,实现刷脸支付、远程开户等便捷服务,优化用户体…...
使用Sum计算Loss和解决梯度累积(Gradient Accumulation)的Bug
使用Sum计算Loss和解决梯度累积的Bug 学习 https://unsloth.ai/blog/gradient:Bugs in LLM Training - Gradient Accumulation Fix 这篇文章的记录。 在深度学习训练过程中,尤其是在大批量(large batch)训练中,如何高…...
mfc操作json示例
首先下载cJSON,加入项目; 构建工程,如果出现, fatal error C1010: unexpected end of file while looking for precompiled head 在cJSON.c文件的头部加入#include "stdafx.h"; 看情况,可能是加到.h或者是.cpp文件的头部,它如果有包含头文件, #include &…...
C语言练习(18)
一个班10个学生的成绩,存放在一个一维数组中,要求找出其中成绩最高的学生成绩和该生的序号。 #include <stdio.h>#define STUDENT_NUM 10 // 定义学生数量int main() {int scores[STUDENT_NUM]; // 定义存储学生成绩的一维数组int i;// 输入10个…...
LeetCode 热题 100_全排列(55_46_中等_C++)(递归(回溯))
LeetCode 热题 100_两数之和(55_46) 题目描述:输入输出样例:题解:解题思路:思路一(递归(回溯)): 代码实现代码实现(思路一(…...
编译chromium笔记
编译环境: windows10 powershell7.2.24 git 2.47.1 https://storage.googleapis.com/chrome-infra/depot_tools.zip 配置git git config --global user.name "John Doe" git config --global user.email "jdoegmail.com" git config --global …...
PHP语言的数据库编程
PHP语言的数据库编程 引言 随着互联网的发展,动态网站已成为主流,而动态网站的核心就是与数据库进行交互。PHP(超文本预处理器)是一种流行的开源服务器端脚本语言,被广泛用于Web开发。它以其简单易学和功能强大而受到…...
【PGCCC】PostgreSQL 中表级锁的剖析
本博客解释了 PostgreSQL 中的锁定机制,重点关注数据定义语言 (DDL) 操作所需的表级锁定。 锁定还是解锁的艺术? 人们通常将数据库锁与物理锁进行比较,这甚至可能导致您订购有关锁的历史、波斯锁和撬锁技术的书籍。我们大多数人可能都是通过…...
1.10 自洽性(Self-Consistency):多路径推理的核心力量
自洽性(Self-Consistency):多路径推理的核心力量 随着人工智能尤其是大规模语言模型的不断进化,如何提升其推理能力和决策准确性成为了研究的重点。在这一背景下,**自洽性(Self-Consistency)**作为一种新的推理方法,逐渐展现出其强大的潜力。自洽性方法通过多路径推理…...
【24】Word:小郑-准考证❗
目录 题目 准考证.docx 邮件合并-指定考生生成准考证 Word.docx 表格内容居中表格整体相较于页面居中 考试时一定要做一问保存一问❗ 题目 准考证.docx 插入→表格→将文本转换成表格→✔制表符→确定选中第一列→单击右键→在第一列的右侧插入列→布局→合并单元格&#…...
Linux 信号(Signal)详解
信号(Signal)是 Linux 系统中用于进程间通信的一种机制。它是一种异步通知,用于通知进程发生了某个事件。信号可以来自内核、其他进程或进程自身。 信号的基本概念 信号的作用: 通知进程发生了某个事件(如用户按下 Ct…...
【数据分享】1929-2024年全球站点的逐年最低气温数据(Shp\Excel\免费获取)
气象数据是在各项研究中都经常使用的数据,气象指标包括气温、风速、降水、湿度等指标!说到气象数据,最详细的气象数据是具体到气象监测站点的数据! 有关气象指标的监测站点数据,之前我们分享过1929-2024年全球气象站点…...
app版本控制java后端接口版本管理
java api version 版本控制 java接口版本管理 1 自定义 AppVersionHandleMapping 自定义AppVersionHandleMapping实现RequestMappingHandlerMapping里面的方法 public class AppVersionHandleMapping extends RequestMappingHandlerMapping {Overrideprotected RequestCondit…...
2024年度总结-CSDN
2024年CSDN年度总结 Author:OnceDay Date:2025年1月21日 一位热衷于Linux学习和开发的菜鸟,试图谱写一场冒险之旅,也许终点只是一场白日梦… 漫漫长路,有人对你微笑过嘛… 文章目录 2024年CSDN年度总结1. 整体回顾2…...
基于python的博客系统设计与实现
摘要:目前,对于信息的获取是十分的重要,我们要做到的不是裹足不前,而是应该主动获取和共享给所有人。博客系统就能够实现信息获取与分享的功能,博主在发表文章后,互联网上的其他用户便可以看到,…...
服务器日志自动上传到阿里云OSS备份
背景 公司服务器磁盘空间有限,只能存近15天日志,但是有时需要查看几个月前的日志,需要将服务器日志定时备份到某个地方,需要查询的时候有地方可查。 针对这个问题,想到3个解决方法: 1、买一个配置比较低…...
优化使用 Flask 构建视频转 GIF 工具
优化使用 Flask 构建视频转 GIF 工具 优化后的项目概述 在优化后的版本中,我们将实现以下功能: 可设置每个 GIF 的帧率和大小:用户可以选择 GIF 的帧率和输出大小。改进的用户界面:使用更现代的设计使界面更美观、整洁。自定义…...
leetcode:511. 游戏玩法分析 I
难度:简单 SQL Schema > Pandas Schema > 活动表 Activity: ----------------------- | Column Name | Type | ----------------------- | player_id | int | | device_id | int | | event_date | date | | games_playe…...
windows git bash 使用zsh 并集成 oh my zsh
参考了 这篇文章 进行配置,记录了自己的踩坑过程,并增加了 zsh-autosuggestions 插件的集成。 主要步骤: 1. git bash 这个就不说了,自己去网上下,windows 使用git时候 命令行基本都有它。 主要也是用它不方便&…...
【Python运维】Python与网络监控:如何编写网络探测与流量分析工具
《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 随着互联网技术的快速发展,网络性能的监控与分析成为保障信息系统稳定运行的关键环节。本文深入探讨了如何利用Python语言构建高效的网络探…...
OpenCV相机标定与3D重建(61)处理未校准的立体图像对函数stereoRectifyUncalibrated()的使用
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 为未校准的立体相机计算一个校正变换。 cv::stereoRectifyUncalibrated 是 OpenCV 库中的一个函数,用于处理未校准的立体图像对。该函…...
字玩FontPlayer开发笔记12 Vue3撤销重做功能
字玩FontPlayer开发笔记12 Vue3撤销重做功能 字玩FontPlayer是笔者开源的一款字体设计工具,使用Vue3 ElementUI开发,源代码:github | gitee 笔记 撤销重做功能是设计工具必不可少的模块,以前尝试使用成熟的库实现撤销重做功能…...
无人机图传模块:深入理解其工作原理与实际效用
无人机图传模块作为无人机系统的关键组成部分,承担着将无人机拍摄的图像和视频实时传输至地面控制站或接收设备的重任。本文将深入探讨无人机图传模块的工作原理及其在实际应用中的效用,帮助读者更好地理解这一技术的奥秘。 一、无人机图传模块的工作原…...
PDF文件提取开源工具调研总结
概述 PDF是一种日常工作中广泛使用的跨平台文档格式,常常包含丰富的内容:包括文本、图表、表格、公式、图像。在现代信息处理工作流中发挥了重要的作用,尤其是RAG项目中,通过将非结构化数据转化为结构化和可访问的信息࿰…...
Linux(Centos 7.6)命令详解:dos2unix
1.命令作用 将Windows格式文件件转换为Unix、Linux格式的文件(也可以转换成其他格式的) 2.命令语法 Usage: dos2unix [options] [file ...] [-n infile outfile ...] 3.参数详解 options: -c, --convmode,转换方式,支持ascii, 7bit, iso, mac,默认…...
梯度提升决策树树(GBDT)公式推导
### 逻辑回归的损失函数 逻辑回归模型用于分类问题,其输出是一个概率值。对于二分类问题,逻辑回归模型的输出可以表示为: \[ P(y 1 | x) \frac{1}{1 e^{-F(x)}} \] 其中 \( F(x) \) 是一个线性组合函数,通常表示为ÿ…...
跨域问题分析及解决方案
1、跨域 指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。 2、同源策略:是指协议,域名,端口都要相同,其中有一个不同都会产生跨域; 3、跨域流程…...
【三国游戏——贪心、排序】
题目 代码 #include <bits/stdc.h> using namespace std; using ll long long; const int N 1e510; int a[N], b[N], c[N]; int w[4][N]; int main() {int n;cin >> n;for(int i 1; i < n; i)cin >> a[i];for(int i 1; i < n; i)cin >> b[i…...
深入理解 Java 的数据类型与运算符
Java学习资料 Java学习资料 Java学习资料 在 Java 编程中,数据类型与运算符是构建程序的基础元素。它们决定了数据在程序中的存储方式以及如何对数据进行各种操作。 一、数据类型 (一)基本数据类型 整型: 用于存储整数数值&…...
WOA-CNN-GRU-Attention、CNN-GRU-Attention、WOA-CNN-GRU、CNN-GRU四模型对比多变量时序预测
WOA-CNN-GRU-Attention、CNN-GRU-Attention、WOA-CNN-GRU、CNN-GRU四模型对比多变量时序预测 目录 WOA-CNN-GRU-Attention、CNN-GRU-Attention、WOA-CNN-GRU、CNN-GRU四模型对比多变量时序预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 基于WOA-CNN-GRU-Attention、…...
(二叉树)
我们今天就开始引进一个新的数据结构了:我们所熟知的:二叉树; 但是我们在引进二叉树之前我们先了解一下树; 树 树的概念和结构: 树是⼀种⾮线性的数据结构,它是由 n ( n>0 ) …...
Linux shell 批量验证端口连通性
脚本 #!/bin/bash # #database check #set -o nounset LOCALIPifconfig | grep inet | head -1 | awk {print $2} | sed s/addr\:// IPLIST192.168.1.99 192.168.1.98 192.168.1.97 PORTLIST81 82 83 84 85 86 check_nc(){ for CHECK_IP in $IPLIST dofor CHECK_PORT in $PORT…...
Java 中实体类与操作类分离
目录 一、为啥要把实体类和操作类分开 二、实体类长啥样,怎么用 三、操作类的使命与实现 四、实战演练:实体类与操作类协同工作 五、拓展思考:这种分离带来的好处与进一步优化 六、总结与展望 家人们,今天我想跟你们唠唠我在…...
创建 pdf 合同模板
创建 pdf 合同模板 一、前言二、模板展示三、制作过程 一、前言 前段时间要求创建“pdf”模板,学会了后感觉虽然简单,但开始也折腾了好久,这里做个记录。 二、模板展示 要创建这样的模板 三、制作过程 新建一个“Word”,这里命…...