protobuf学习

news2024/11/26 13:45:08

protobuf的安装

  • Windos版安装
    下载地址:https://github.com/protocolbuffers/protobuf/releases 选择合适的版本下载
    将下载的压缩包解压,把解压后文件的bin目录的路径配置到系统环境比变量Path中
    在cmd 中执行 protoc --version 成功就说明安装成功了
  • linux版安装
  1. 安装依赖库
    Ubuntu
    sudo apt-get install autoconf automake libtool curl make g++ unzip -y
    CentOs
    sudo yum install autoconf automake libtool curl make gcc-c++ unzip
  2. 下载安装包
    也是到github protobuf的仓库找到合适的包,然后执行下面的命令
    wget https://github.com/protocolbuffers/protobuf/releases/download/v21.11/protobuf-all-
    21.11.zip
  3. 解压安装包
    解压后进入protobuf目录,之后如果安装全语言的就执行 ./autogen.sh 否则忽略
    执行 ./configure 或者 ./configure --prefix=/usr/local/protobuf
    前者是默认安装路径 后者是修改安装⽬录,统⼀安装在/usr/local/protobuf下
    下面依次执行
    make
    make check
    sudo make install
    如果在执行./configure是选择第二种方式那么还需要在/etc/profile 中添加⼀些内容
    sudo vim /etc/profile
#(动态库搜索路径) 程序加载运⾏期间查找动态链接库时指定除了系统默认路径之外的其他路径
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/protobuf/lib/
#(静态库搜索路径) 程序编译期间查找动态链接库时指定查找共享库的路径
export LIBRARY_PATH=$LIBRARY_PATH:/usr/local/protobuf/lib/
#执⾏程序搜索路径
export PATH=$PATH:/usr/local/protobuf/bin/
#c程序头⽂件搜索路径
export C_INCLUDE_PATH=$C_INCLUDE_PATH:/usr/local/protobuf/include/
#c++程序头⽂件搜索路径
export CPLUS_INCLUDE_PATH=$CPLUS_INCLUDE_PATH:/usr/local/protobuf/include/
#pkg-config 路径
export PKG_CONFIG_PATH=/usr/local/protobuf/lib/pkgconfig/

最后⼀步,重新执⾏ /etc/profile ⽂件
source /etc/profile
protoc --version 查看版本,有显⽰说明安装成功

protobuf 使用

步骤一:创建.proto文件

在一个目录下编写.proto文件

syntax="proto3";  // 首行指定 proto3 语法  否则默认使用proto2语法
package contacts;  // package 是⼀个可选的声明符,能表⽰ .proto ⽂件的命名空间,在项⽬中要有唯⼀性。它的作⽤是为
                    //了避免我们定义的消息出现冲突。

            
message PeopleInfo{   // 定义消息 就是我们要定义的结构化对象,要序列化反序列化的属性内容定义在其中
    string name=1;   //所以 ProtoBuf 就是以 message 的⽅式来⽀持我们定制协议字段,后期帮助我们形成类和⽅法来使⽤
    int32 age=2;
}
  • 定义消息字段
    在 message 中我们可以定义其属性字段,字段定义格式为:字段类型 字段名 = 字段唯⼀编号;
  1. 字段类型分为:标量数据类型 和 特殊类型(包括枚举、其他消息类型等)
  2. 字段唯⼀编号:⽤来标识字段,⼀旦开始使⽤就不能够再改变。
  • 消息字段标量数据类型与C++中对应的类型
.proto TypeNotesC++ Type
doubledouble
floatfloat
int32使⽤变⻓编码[1]。负数的编码效率较低⸺若字段可能为负值,应使⽤ sint32 代替int32
int64使⽤变⻓编码[1]。负数的编码效率较低⸺若字段可能为负值,应使⽤ sint64 代替int64
uint32使⽤变⻓编码[1]uint32
uint64使⽤变⻓编码[1]uint64
sint32使⽤变⻓编码[1]。符号整型。负值的编码效率⾼于常规的 int32 类型int32
sint64使⽤变⻓编码[1]。符号整型。负值的编码效率⾼于常规的 int64 类型int64
fixed32定⻓ 4 字节。若值常⼤于2^28 则会⽐ uint32 更⾼效uint32
fixed64定⻓ 8 字节。若值常⼤于2^56 则会⽐ uint64 更⾼效uint64
sfixed32定长4字节int32
sfixed64定长8字节int64
boolbool
string包含 UTF-8 和 ASCII 编码的字符串,⻓度不能超过2^32string
bytes可包含任意的字节序列但⻓度不能超过 2^32string

变⻓编码是指:经过protobuf 编码后,原本4字节或8字节的数可能会被变为其他字节数
字段唯一编号的范围
1 ~ 536,870,911 (2^29 - 1) ,其中 19000 ~ 19999 不可⽤,为预留标识号,如果设置了这些编号,编译会报警,1~15只需一个字节进行编码,16 ~ 2047 内的数字需要两个字节进⾏编码。编码后的字节不仅只包含了编号,还包含了字段类型,所以为了提高传输效率应尽量使用小的编号

步骤2:编译 contacts.proto ⽂件,⽣成 C++ ⽂件

protoc [--proto_path=IMPORT_PATH] --cpp_out=DST_DIR path/to/file.proto
protoc       # 是 Protocol Buffer 提供的命令⾏编译⼯具。
--proto_path # 指定 被编译的.proto⽂件所在⽬录,可多次指定。
#可简写成 -I IMPORT_PATH 。如不指定该参数,则在当前⽬录进⾏
#搜索。当某个.proto ⽂件 import 其他.proto ⽂件时,或需要编译的 .proto ⽂件不在当前⽬录下,这时就要⽤-I来指定搜索⽬录。

--cpp_out=   # 指编译后的⽂件为 C++ ⽂件
OUT_DIR           #编译后⽣成⽂件的⽬标路径。
path/to/file.proto        # 要编译的.proto⽂件。
在当前路径编译 contacts.proto
protoc  --cpp_out=.   contacts.proto
在其他路径编译这个contacts.proto
protoc -I  /root/data/protobuf/fast_start --cpp_out= /root/data/protobuf/fast_start  contacts.proto

编译成功 生成相应的.h .cc文件

在这里插入图片描述
对于编译⽣成的 C++ 代码,包含了以下内容 :
• 对于每个 message ,都会⽣成⼀个对应的消息类。
• 在消息类中,编译器为每个字段提供了获取和设置⽅法,以及⼀下其他能够操作字段的⽅法。
• 编辑器会针对于每个 .proto ⽂件⽣成 .h 和 .cc ⽂件,分别⽤来存放类的声明与类的实现。

  • 每个字段都有设置和获取的⽅法, getter 的名称与⼩写字段完全相同,setter ⽅法以 set_ 开头。

  • 每个字段都有⼀个 clear_ ⽅法,可以将字段重新设置回 empty 状态。

在消息类的⽗类MessageLite 中,提供了读写消息实例的⽅法,包括序列化⽅法和反序列化⽅法。

class MessageLite {
public:
 //序列化:
 bool SerializeToOstream(ostream* output) const; // 将序列化后数据写⼊⽂件
流
 bool SerializeToArray(void *data, int size) const;
 bool SerializeToString(string* output) const;
 
 //反序列化:
 bool ParseFromIstream(istream* input); // 从流中读取数据,再进⾏反序列化
动作
 bool ParseFromArray(const void* data, int size);
 bool ParseFromString(const string& data);
};

• 序列化的结果为⼆进制字节序列,⽽⾮⽂本格式。
• 以上三种序列化的⽅法没有本质上的区别,只是序列化后输出的格式不同,可以供不同的应⽤场景
使⽤。
• 序列化的 API 函数均为const成员函数,因为序列化不会改变类对象的内容, ⽽是将序列化的结果保存到函数⼊参指定的地址中。

步骤3 引入编译生成的头文件测试序列化和反序列化

#include<iostream>
#include<string>
#include"contacts.pb.h"

using namespace std;


int main()
{
    contacts::PeopleInfo posttest;
    posttest.set_age(18);
    posttest.set_name("张三");
    string str;
    if(!posttest.SerializeToString(&str))
    {
        cout<<"Serialize fail"<<endl;
        return -1;
    }
    cout<<"Serialize success\tstr:"<<str<<endl;

    contacts::PeopleInfo recvtest;
    if(!recvtest.ParseFromString(str))
    {
        cout<<"Parse fail"<<endl;
        return -1;
    }
   cout<<"Parse success"<<endl
       <<"age:"<<recvtest.age()<<endl
       <<"name:"<<recvtest.name()<<endl;
    return 0;
}

在这里插入图片描述

proto3 语法

  • 一个.proto文件可以定义多个message ,message可以看成是自定义类型,成为其他message的属性
  • message可以嵌套定义,而且隔离字段编号
  • .proto 可以使用 import 导入其他.proto文件,如果其他.proto文件定义的message使用了命名空间,在导入后只需使用name.message引入想使用的message既可以,没用命名空间隔离就直接使用message
  • message中嵌套message 字段也是通过mutable 返回那个字段的地址来设置
  • singular :消息中可以包含该字段零次或⼀次(不超过⼀次)。 proto3 语法中,字段默认使⽤该规则。也就是非数组类型
  • repeated :消息中可以包含该字段任意多次(包括零次),其中重复值的顺序会被保留。可以理解为定义了⼀个数组 例如 repeated Phone phone 加入的方式是
 contacts::PeopleInfo_Phone* peo_phone = people->add_phone();
 然后设置 peo_phone
 支持迭代器遍历
  for (auto& phone : request.phone()) 
  • enum类型
    在.proto文件中我们可以定义枚举类型
enum PhoneType {
 MP = 0; // 移动电话
 TEL = 1; // 固定电话
}

注意事项

  1. 0 值常量必须存在,且要作为第⼀个元素。这是为了与 proto2 的语义兼容:第⼀个元素作为默认值,且值为 0
  2. 枚举类型可以在消息外定义,也可以在消息体内定义(嵌套)
  3. 枚举的常量值在 32 位整数的范围内。但因负值⽆效因⽽不建议使⽤(与编码规则有关)
    还要注意将两个 ‘具有相同枚举值名称’ 的枚举类型放在单个 .proto ⽂件下测试时,编译后会报错:某某某常量已经被定义!所以这⾥要注意:
    • 同级(同层)的枚举类型,各个枚举类型中的常量不能重名。
    • 单个 .proto ⽂件下,最外层枚举类型和嵌套枚举类型,不算同级。
    • 多个 .proto ⽂件下,若⼀个⽂件引⼊了其他⽂件,且每个⽂件都未声明 package,每个 proto ⽂件中的枚举类型都在最外层,算同级。
    • 多个 .proto ⽂件下,若⼀个⽂件引⼊了其他⽂件,且每个⽂件都声明了 package,不算同级。
    对应数据中没有主动设置时 枚举属性值默认是0值

在默认路径 /usr/local/protobuf/下面有protobuf的一些配置文件
有bin、include、lib
在include下面就有已经写好的各种头文件包括message序列化反序列化的头文件 message.h message_lite.h

  • == Any==类型
    是已经为我们定义好的一种message类型 相关的.proto文件就在上面所说的include 目录下。Any 类型可以理解为泛型类型。使⽤时可以在 Any 中存储任意消息类型。Any 类型的字段也可⽤ repeated 来修饰
    在any.pb.h中可以查看Any类型主要的方法,Any类型就是用来转换其他任意message的一种类型所以有以下的方法
class PROTOBUF_EXPORT Any final : public ::PROTOBUF_NAMESPACE_ID::Message {
 bool PackFrom(const ::PROTOBUF_NAMESPACE_ID::Message& message) {
 ...
 }
 bool UnpackTo(::PROTOBUF_NAMESPACE_ID::Message* message) const {
 ...
 }
 template<typename T> bool Is() const {
 return _impl_._any_metadata_.Is<T>();
 }
};
// 对应any类型,还有has_name 方法在解析时用来判断之前是否有设置过这个any类型
// bool类型返回值,来判断执行是否成功
 //使⽤ PackFrom() ⽅法可以将任意消息类型转为 Any 类型。
 //使⽤ UnpackTo() ⽅法可以将 Any 类型转回之前设置的任意消息类型。
 //使⽤ Is() ⽅法可以⽤来判断存放的消息类型是否为 typename T。
 //message中都有一个mutable_name()方法,对message中这个字段申请号空间返回相应类型地址让我们设置值
  • oneof类型
    如果消息中有很多可选字段, 并且将来同时只有⼀个字段会被设置, 那么就可以使⽤ oneof 加强这
    个⾏为,也能有节约内存的效果,对oneof类型里面的字段设置值,最终只会取最后一次设置的值,oneof类型不能是repeated
 oneof other_contact{  // oneof 中的字段编号与PeopleInfo 中的字段编号是处于同一级别
        string qq=5;
        string weixin=6;
    } 
oneof 类型对应生成的.h中常用的方法有
qq字段就有以下常用类型  weixin 相应的也有这些方法
 bool has_qq() const;
  const std::string& qq() const;
   std::string* mutable_qq();
    template <typename ArgT0 = const std::string&, typename... ArgT>
  void set_qq(ArgT0&& arg0, ArgT... args);

 inline PeopleInfo::OtherContactCase PeopleInfo::other_contact_case() const {
  return PeopleInfo::OtherContactCase(_impl_._oneof_case_[0]);
} //这个是返回设置了oneof中对应字段的字段编号
  • map类型
    语法⽀持创建⼀个关联映射字段,也就是可以使⽤ map 类型去声明字段类型
map<string,string>remark=7;  
key_type 是除了 float 和 bytes 类型以外的任意标量类型。 value_type 可以是任意类型
map 字段不可以⽤ repeated 修饰
 map 中存⼊的元素是⽆序的
有常规的获取方法,设置方法使用mutable_name()、name_size()判断数量
支持迭代器遍历
  • 默认值
    反序列化消息时,如果被反序列化的⼆进制序列中不包含某个字段,反序列化对象中相应字段时,就
    会设置为该字段的默认值。不同的类型对应的默认值不同:
    • 对于字符串,默认值为空字符串。
    • 对于字节,默认值为空字节。
    • 对于布尔值,默认值为 false。
    • 对于数值类型,默认值为 0。
    • 对于枚举,默认值是第⼀个定义的枚举值, 必须为 0。
    • 对于消息字段,未设置该字段。它的取值是依赖于语⾔。
    • 对于设置了 repeated 的字段的默认值是空的( 通常是相应语⾔的⼀个空列表 )。
    • 对于 消息字段 、 oneof字段 和 any字段 ,C++ 和 Java 语⾔中都有 has_ ⽅法来检测当前字段
    是否被设置。标量数据类型没有has_ 方法

  • 更新消息
    如果现有的消息类型已经不再满⾜我们的需求,例如需要扩展⼀个字段,在不破坏任何现有代码的情况下更新消息类型⾮常简单。遵循如下规则即可:
    • 禁⽌修改任何已有字段的字段编号。
    • 若是移除⽼字段,要保证不再使⽤移除字段的字段编号。正确的做法是保留字段编号
    (reserved),以确保该编号将不能被重复使⽤。不建议直接删除或注释掉字段。
    • int32, uint32, int64, uint64 和 bool 是完全兼容的。可以从这些类型中的⼀个改为另⼀个,
    ⽽不破坏前后兼容性。若解析出来的数值与相应的类型不匹配,会采⽤与 C++ ⼀致的处理⽅案
    (例如,若将 64 位整数当做 32 位进⾏读取,它将被截断为 32 位)。
    • sint32 和 sint64 相互兼容但不与其他的整型兼容。
    • string 和 bytes 在合法 UTF-8 字节前提下也是兼容的。
    • bytes 包含消息编码版本的情况下,嵌套消息与 bytes 也是兼容的。
    • fixed32 与 sfixed32 兼容, fixed64 与 sfixed64兼容。
    • enum 与 int32,uint32, int64 和 uint64 兼容(注意若值不匹配会被截断)。但要注意当反序
    列化消息时会根据语⾔采⽤不同的处理⽅案:例如,未识别的 proto3 枚举类型会被保存在消息
    中,但是当消息反序列化时如何表⽰是依赖于编程语⾔的。整型字段总是会保持其的值。
    • oneof:
    ◦ 将⼀个单独的值更改为 新 oneof 类型成员之⼀是安全和⼆进制兼容的。
    ◦ 若确定没有代码⼀次性设置多个值那么将多个字段移⼊⼀个新 oneof 类型也是可⾏的。
    ◦ 将任何字段移⼊已存在的 oneof 类型是不安全的

  • 保留字段reserved
    如果通过删除或注释掉字段来更新消息类型,未来的⽤⼾在添加新字段时,有可能会使⽤以前已经
    存在,但已经被删除或注释掉的字段编号。将来使⽤该 .proto 的旧版本时的程序会引发很多问题:数
    据损坏、隐私错误等等。
    确保不会发⽣这种情况的⼀种⽅法是:使⽤ reserved 将指定字段的编号或名称设置为保留项 。当
    我们再使⽤这些编号或名称时,protocol buffer 的编译器将会警告这些编号或名称不可⽤。举个例
    ⼦:

message test
{
  reserved 10,11,20,50 to 100;  # 保留字段和名称,后续就不能在使用了
  reserved "field1","field2";
}

protobuf版通讯录

syntax="proto3";
package contacts2;



message PeopleInfo
{
    int32 age=1;
    string name=2;
    message Phone
    {
        string number=1;
    }
    repeated Phone phone=3;
}

message contacts
{
    repeated PeopleInfo people=1;
}

writefile.cc

syntax="proto3";
package contacts2;



message PeopleInfo
{
    int32 age=1;
    string name=2;
    message Phone
    {
        string number=1;
    }
    repeated Phone phone=3;
}

message contacts
{
    repeated PeopleInfo people=1;
}

read.cc

#include <iostream>
#include <fstream>
#include "contacts.pb.h"

using namespace std;

void printinfo(const contacts2::contacts &info)
{
    for (int i = 0; i < info.people_size(); ++i) // 数组类型字段,用 属性名_size() 获取数量
    {
        cout << "第" << i + 1 << "联系人信息:" << endl;
        const contacts2::PeopleInfo &people = info.people(i); // 取出数组元素i为值
        cout << "age:" << people.age() << endl;
        cout << "name:" << people.name() << endl;
        for (int j = 0; j < people.phone_size(); ++j)//也可以直接使用范围for 
        {
            const contacts2::PeopleInfo_Phone &phone = people.phone(j);
            cout << "phone:" << j + 1 << "\t" << phone.number() << endl;
        }
    }
}
int main()
{
    contacts2::contacts data;
    fstream input("contacts.bin", ios::in | ios::binary);
    if (!input)
    {
        cout << "open fail" << endl;
        return -1;
    }
    if (!data.ParseFromIstream(&input))
    {
        cout << "Parse fail" << endl;
        input.close();
        return -1;
    }
    printinfo(data);
    input.close();
    return 0;
}

–decode protoc的参数

表⽰从标准输⼊中读取给定类型的⼆进制消息,并将其以⽂本格式写⼊
标准输出。 消息类型必须在 .proto ⽂件或导⼊的⽂件中定义,我们可以选择从文件中读取二进制消息,这样就不用再写read.cc来解析文件后打印来看了

 protoc --decode=contacts2.contacts contacts.proto < contacts.bin
 # =后面是命名空间.message 后面给出.proto文件,从contacts.bin中读取
 #转换为文本格式输入到标准输出

在这里插入图片描述

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

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

相关文章

Vue.js基础简答题

系列文章目录 后续补充 文章目录 系列文章目录前言一、库与框架的区别是什么&#xff1f;二、Vue.js 的核心特性有哪些&#xff1f;三、什么是数据驱动视图&#xff1f;四、MVVM 模型各部分含义是什么&#xff0c;在 Vue.js 中分别对应哪些功能&#xff1f;五、el 选项的作用是…

一)Stable Diffusion使用教程:安装

目前AI绘画最火的当属Midjorney和Stable Diffusion&#xff0c;但是由于Midjourney没有开源&#xff0c;因此我们主要分享下Stable Diffusion&#xff0c;后面有望补上Midjourney教程。 本节主要讲解Stable Diffusion&#xff08;以下简述SD&#xff09;的下载和安装。 1&…

uniapp查看ios打包后的Info.plist文件

最近在用 uni 开发 ios 的时候给项目添加了自定义的 Info.plist 文件&#xff0c;但是打包后发现并没有生效&#xff0c;才有了查看打包后的 Info.plist 文件想法。 HBuilderX3.6.5起&#xff0c;支持直接在应用项目中配置 iOS 平台的 Info.plist 和 资源文件&#xff08;Bundl…

pytest中生成allure报告时,测试报告中统计的用例数不正确

【问题描述】&#xff1a;pytest中生成allure报告时&#xff0c;测试报告中统计的用例数不正确&#xff0c;用例数总是比实际用例数多 【问题定位】&#xff1a;因为生成index.html的allure报告&#xff0c;是根据临时的json文件生成的。每次运行时&#xff0c;没有删除旧的js…

Grafana图形web监控的安装与配置

目录 一、安装并配置 二、Web访问 三、Grafana启用zabbix插件 四、Grafana添加zabbix数据源 五、创建仪表盘 创建监控项完成保存仪表盘 六、查看创建的仪表盘 七、在现有的dashboard&#xff08;仪表盘&#xff09;中添加图形 八、查看最终dashborad&#xff08;仪表盘&#x…

react 实现小球加入购物车动画

代码 import React, { useRef } from react;const ProductLayout () > {const box useRef(null);const createBall (left, top) > {const ball document.createElement(div);ball.style.position absolute;ball.style.left left - 10 px;ball.style.top top - 1…

三维点云中的坐标变换(只讲关键部分)

一、坐标旋转 坐标旋转包含绕x、y、z轴旋转&#xff0c;在右手坐标系中&#xff0c;x-翻滚(roll)&#xff0c;y-俯仰(pitch)&#xff0c;z-航向(yaw)。如果想详细了解&#xff0c;可以网络搜索 在PCL中&#xff0c;从baseLink到map的转换关系为:先绕x轴旋转,在绕y轴旋转,最后绕…

linux:secureCRT通过pem证书远程访问服务器

参考&#xff1a; secureCRT通过pem证书远程访问服务器_Fengshana的博客-CSDN博客 总结&#xff1a; 配置公钥即可

hdu7298 Coin(网络流+按时间拆点)

题目 t(t<10)组样例&#xff0c;每次给n(n<3e3)个人&#xff0c; 第i个人&#xff0c;在任意时刻&#xff0c;都最多只能有ai(1<ai<3e3)个硬币 其中k(k<n)个是小F的朋友&#xff0c;依次用点号的形式给出 初始时&#xff0c;每个人都有一个硬币&#xff0c;…

<j-editable-table 新增行 按钮去掉方法

新增行 按钮去掉方法 修改的内容 要去掉组件中的新增行添加按钮&#xff0c;你可以将:actionButton"true"的值改为:actionButton“false”&#xff0c;即将true改为false&#xff0c; 去掉前的 <j-editable-tableref"editableMeTable":loading"m…

react中使用echarts

下载插件 npm install echarts npm install echarts-for-react 引入模块 import ReactEcharts from "echarts-for-react" import echarts from "echarts"; import React from react; import ReactEcharts from echarts-for-react const Tiao () >…

LabelImg 标注工具的使用(获取标注的图片数据集)

目录 1 安装及使用1.1 安装1.2 使用 2 标注格式 1 安装及使用 1.1 安装 Win R输入cmd进入终端界面 LabelImg 安装命令&#xff1a; pip install labelimg没有配置国内 pip 源的&#xff0c;请看 ⇒ \Rightarrow ⇒ 临时办法&#xff1a;在上述命令末尾添加源&#xff0c…

分布式系统的应用程序性能监视工具-skywalking

分布式系统的应用程序性能监视工具,专为微服务、云原生架构和基于容器(Docker、K8s、Mesos)架构而设计。提供分布式链路日志追踪、剖析、服务网格遥测分析、度量聚合和可视化一体化解决方案。 在应用系统进行实时监控,实现对应用性能管理和故障定位的系统化解决方案中常用的…

【C语言敲重点(五)】嵌入式“八股文“(2)

1. struct和union的区别&#xff1f; 答&#xff1a;①联合体所有的成员都共享一块内存&#xff0c;修改联合体的任一成员的数据就会覆盖到其他成员的数据&#xff1b; ②结构体的成员变量都有独立的内存空间&#xff0c;且结构体的成员数据之间是不影响的 2. struct和class的…

HTTPS——HTTPS如何加密数据,“证书“为什么可以应对 “中间人攻击“

文章目录 前言一、HTTPS是什么二、HTTPS加密方法三、HTTPS加密流程对称加密非对称加密中间人攻击证书 总结 前言 本人是一个普通程序猿!分享一点自己的见解,如果有错误的地方欢迎各位大佬莅临指导,如果你也对编程感兴趣的话&#xff0c;互关一下&#xff0c;以后互相学习&…

Linux笔记——管道相关命令以及shell编程

文章目录 管道相关命令 目标 准备工作 1 cut 1.1 目标 1.2 路径 1.3 实现 2 sort 2.1 目标 2.2 路径 2.3 实现 第一步: 对字符串排序 第二步&#xff1a;去重排序 第三步: 对数值排序 默认按照字符串排序 升序 -n 倒序 -r 第四步: 对成绩排序【按照列排序】 …

python实现逻辑回归-清风数学建模-二分类水果数据

所用数据 &#x1f449;&#x1f449;&#x1f449;二分类水果数据 1.数据预处理 可以看到有4个特征&#xff0c;2种分类结果&#xff0c;最后4个没有分类结果的数据是拿来预测的 # 1. 数据预处理 import pandas as pd df pd.read_excel(oridata/二分类水果数据.xlsx,use…

启用window10专业版系统自带的远程桌面

启用window10专业版系统自带的远程桌面 Windows操作系统作为应用最广泛的个人电脑操作系统&#xff0c;在我们身边几乎随处可见。虽然近些年因手机、平板电脑等设备的兴起&#xff0c;个人电脑的存在感逐渐降低&#xff0c;但对于一些大型程序和特殊情况&#xff08;办公软件、…

超细Python性能测试实战,Locust框架性能测试(详全)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 当涉及到评估应用…

《零基础入门学习Python》第062讲:论一只爬虫的自我修养10:安装Scrapy

这节课我们来谈谈 Scrapy 说到Python爬虫&#xff0c;大牛们都会不约而同地提起Scrapy。因为Scrapy是一个为了爬取网站数据&#xff0c;提取结构性数据而编写的应用框架。可以应用在包括数据挖掘&#xff0c;信息处理或存储历史数据等一系列的程序中。 Scrapy最初是为了页面抓…