一次性搞懂
一次性搞懂
常见Web请求方式
FunctionCall SSE 长连接 websocket HTTP长连接 短连接 HTTP/1.1、HTTP/2、HTTP/3、WebSocket、TLS RPC GraphQL SOAP MQTT WebRTC
文本传输协议
XML
该入土的老东西早该爆金币了(
凑活看mozilla的文档吧,下面有其他文档的引用。 XML 树结构 | 菜鸟教程 有且仅有一个根节点,大小写敏感,第一行必是版本和编码,保证严格有序,元素可以重名解析的时候视为列表,xml以LF存储换行。
特殊符号< <
> >
& &
' &apos
" "
代替
所有的节点名,InnerText,Attributes都是字符串类型,IO时的类型转换都由程序来做。
虽然格式很自由但是建议所有人用一个风格。
<?xml version="1.0" encoding="UTF-8"?> <!-- 第一行固定必须写 -->
<message>
<Friend name="小明" age='8'>我的朋友</Friend><!-- 节点可以带属性 -->
<Fater name="爸爸" age="50" /><!-- 没有子节点可以和html一样关闭 -->
<warning>
Hello World
</warning>
</message>
神秘古老且恶心的CDATA;XSD;DTD;SOAP
CDATA(CharacterData):被这东西包裹的内容不会被转义,会被原样读取出来
<query><![CDATA[
SELECT * FROM user WHERE age > 18;
]]>惺惺相惜</query><!--可以和一般的混着用-->
XSD(XMLSchemaDefinition):新生代用来替代DTD校验XML的东西,纯规则没有数据。
<?xml version="1.0"?>
<!-- 根元素:声明这是一个 XML Schema 文件 -->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- xs:element:定义一个元素 -->
<xs:element name="user">
<!-- xs:complexType:复杂类型,元素可以有子元素或属性 -->
<xs:complexType>
<!-- xs:sequence:子元素必须按顺序出现 -->
<xs:sequence>
<!-- 普通元素,直接指定类型 -->
<xs:element name="name" type="xs:string"/>
<!-- 元素内部定义简单类型限制 -->
<xs:element name="age">
<!-- xs:simpleType:简单类型,可以在里面加 restriction 限制 -->
<xs:simpleType>
<!-- xs:restriction:对基础类型加限制 -->
<xs:restriction base="xs:int">
<!-- xs:minInclusive / xs:maxInclusive:最小/最大值 -->
<xs:minInclusive value="0"/>
<xs:maxInclusive value="120"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="gender">
<xs:simpleType>
<xs:restriction base="xs:string">
<!-- xs:enumeration:指定可选枚举值 -->
<xs:enumeration value="男"/>
<xs:enumeration value="女"/>
<xs:enumeration value="未知"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
<!-- xs:attribute:为元素添加属性 -->
<xs:attribute name="id" type="xs:int" use="required"/>
</xs:complexType>
</xs:element>
</xs:schema>
DTD(DocumentTypeDefinition): 上世纪80-90留下的历史债,规定了数据的格式类型顺序等等,不仅是规则还混合了一些数据。比较恶心用的也很少了,形式类似于这种<!ELEMENT user (name, age)> <!ATTLIST user id ID #REQUIRED>
(看不懂就对了。
SOAP(SimpleObjectAccessProtocol): 使用纯XML通信的web协议,格式类似于下面这种,有时候还会在header中带东西,也比较古老且恶心。
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
...
</s:Body>
</s:Envelope>
JSON
文档在这里 json标准东西不多
json的类型支持 字符串(string)、数值(number)、true
、false
、 null
、字典(object)或者数组(array)。这些结构可以嵌套。
在 JSON 传输过程中,列表是有序的,但字典(对象)不是有序的。JSON本身没有明确的长度限制,但是也不会写的巨长,序列化的时候一般会放内存容易崩
Protobuf
中文翻译文档和英文原版文档。
这个博客不错,给出了最佳实践和必要的编码原理 google开发的,和json相比体积小速度快,适合IM这种延迟敏感型应用,proto文件是跨语言的,定义好之后可以用(谷歌提供的)工具反向生成各种语言的代码,再结合proto的包去使用,所以说一开始格式就已经被确定了。
📝Note
proto有2和3两个版本是不兼容的,现在绝大部分用的是proto3,如果碰到奇怪的文件请注意proto版本的可能性
注意文档中的协议特性和不适用的地方!!!!!
1 ~ 15:单字节编码,16 ~ 2047:双字节编码,使用频率高的变量最好设置为1~15,编号一旦指定不能修改,所以为了扩展保留一些 1~15 的 编号
Protobuf在消息大小上没有明确限制,但通常建议保持小于2MB(就是不要太大,并不一定非得2MB)。Protobuf的字段顺序在定义时是固定的,但在序列化时不保证顺序
C#代码中可以使用protogen,是针对C#的实现,大致看一眼,用到细节再去查。mac上brew install protobuf
安装,使用protoc --version
查看版本,一般是3.
特性 | protoc | protogen |
---|---|---|
生成的代码 | 标准的 Protocol Buffers C# 类 | 生成的 C# 类更符合 .NET 的风格和最佳实践 |
gRPC 支持 | 支持 gRPC(通过 grpc_csharp_plugin) | 提供更丰富的 gRPC 支持,优化了与 .NET 集成的方式 |
定制化能力 | 基本的 C# 类生成,较少定制化 | 提供更多的扩展点,允许开发者对代码进行自定义 |
.NET 特性 | 提供基础的 C# 支持 | 更好支持 .NET 特性,如依赖注入、日志系统等 |
集成工具 | 与原生 Protobuf 代码生成集成 | 与 .NET 工具链更深度集成,比如 MSBuild 插件 |
使用场景 | 适用于跨平台和多语言项目 | 更适合 .NET 核心或大型 C# 项目,尤其是 gRPC 服务 |
一个基本的protobuf文件的定义如下
syntax = "proto3";
package example; // 包可以划分不同的命名空间
// import "user.proto"; // 可以引入其他的包
// 定义一个用户类型的枚举
enum UserType {
UNKNOWN = 0;
ADMIN = 1;
MEMBER = 2;
}
// 地址信息消息
message Address {
string street = 1;
string city = 2;
string state = 3;
sint32 zip_code = 4; // 适合表示带负数的
}
// 用户信息消息,嵌套Address
message User {
int32 id = 1; // 比较适合正数
string name = 2;
string email = 3;
repeated Address addresses = 4; // 嵌套的Address消息类型
UserType type = 5; // 使用枚举类型UserType
}
proto文件消息类型 | C++ 类型 | 说明 |
---|---|---|
double | double | 双精度浮点型 |
float | float | 单精度浮点型 |
int32 | int32 | 使用可变长编码方式,负数时不够高效,应该使用sint32 |
int64 | int64 | 使用可变长编码方式,负数时不够高效,应该使用sint32 |
uint32 | uint32 | 使用可变长编码方式 |
uint64 | uint64 | 使用可变长编码方式 |
sint32 | int32 | 使用可变长编码方式,有符号的整型值,负数编码时比通常的int32高效 |
sint64 | sint64 | 使用可变长编码方式,有符号的整型值,负数编码时比通常的int64高效 |
fixed32 | uint32 | 总是4个字节,如果数值总是比2^28大的话,这个类型会比uint32高效 |
fixed64 | uint64 | 总是8个字节,如果数值总是比2^56大的话,这个类型会比uint64高效 |
sfixed32 | int32 | 总是4个字节 |
sfixed64 | int64 | 总是8个字节 |
bool | bool | 布尔类型 |
string | string | 一个字符串必须是utf-8编码或者7-bit的ascii编码的文本 |
bytes | string | 可能包含任意顺序的字节数据,不是C#中的字节数组 |
在 Protocol Buffers (Protobuf) 中,生成的代码会根据目标语言的命名约定来调整字段名。在 C# 中,protoc
会将消息和字段名转换为首字母大写,以符合 C# 的命名约定。
- 消息名:
message hello_world_request
会变为HelloWorldRequest
。
- 字段名:
helloworld
会变为Helloworld
。- 如果字段是用下划线(
_
)分隔的,例如hello_world
,会变成HelloWorld
。
这是 Protobuf 的 protoc
编译器针对 C# 的正常行为,不会影响字段实际存储或序列化的键值,只是对代码接口做了语言风格适配。
MessagePack
跨语言,定义自带类型,支持动态解析,比json快很多,纯2进制传输,相比protobuf稍大但灵活,速度比proto慢一点。
常用于对延迟和体积有要求的地方比如rpc等,感觉需要速度的时候都可以被proto替代,不需要速度的时候被json替代。可能碰到恰好的场景才会发挥作用。
- key不能重复或者跳号,可以用字符串当key但是体积大;
- 字段增加用新的 Key,旧 Key 保留或废弃,否则重用可能导致历史数据错误;
- 兼容了特殊字符;
- 反序列化的时候可能报错要注意处理
- 对引用类型可以兼容null,值类型必须标记可空才行否则报错
用到的时候再去搞细节吧,C#世界里有MemoryPack上位替代
在C#中的示例
[MessagePackObject]
public class User
{
[Key(0)] public int? Id { get; set; } // 值类型可空标记
[Key(1)] public string Name { get; set; }
[Key(2)] public string Email { get; set; }
[Key(3)] public Animal Pet { get; set; } // 使用继承类的时候一定要注意
}
[Union(0, typeof(Dog))]
[Union(1, typeof(Cat))]
[MessagePackObject]
public abstract class Animal
{ [Key(0)] public string Name { get; set; } }
[MessagePackObject]
public class Dog : Animal
{ [Key(1)] public bool HasTail { get; set; } }
[MessagePackObject]
public class Cat : Animal
{ [Key(1)] public int Lives { get; set; } }
class Program
{
static void Main(string[] args)
{
User user = new User { Id = 1001, Name = "Jack", Email = "jack@example.com" ,Pet = new Dog { Name = "Buddy", HasTail = true }}; // 创建对象,注意看是怎么多态指定Pat的类型
// 序列化到字节数组
byte[] bytes = MessagePackSerializer.Serialize(user);
Console.WriteLine("序列化字节长度: " + bytes.Length);
// 反序列化
User deserializedUser = MessagePackSerializer.Deserialize<User>(bytes);
Console.WriteLine($"反序列化结果: Id={deserializedUser.Id}, Name={deserializedUser.Name}, Email={deserializedUser.Email}");
// 序列化到文件
string filePath = "user.dat";
File.WriteAllBytes(filePath, bytes);
Console.WriteLine("已写入文件: " + filePath);
// 从文件读取并反序列化
byte[] readBytes = File.ReadAllBytes(filePath);
User userFromFile = MessagePackSerializer.Deserialize<User>(readBytes);
Console.WriteLine($"从文件读取: Id={userFromFile.Id}, Name={userFromFile.Name}, Email={userFromFile.Email}");
}
}
编码的发展
有时间再整理。 彻底摆脱乱码的困惑-技术圈
乱码对照表_evasnowind的博客-CSDN博客_乱码对照表
ASCII Table - ASCII Character Codes, HTML, Octal, Hex, Decimal
字符串和编码 - 廖雪峰的官方网站
一个汉字占多少
ASCII 要记住什么
Unicode中文乱码表
乱码 | 示例 | 特点 | 产生原因 |
---|---|---|---|
古文码 | 鐢辨湀瑕佸ソ濂藉涔犲ぉ澶╁悜涓? | 大都为不认识的古文,并加杂日韩文 | 以 GBK 方式读取 UTF-8 编码的中文 |
口字码 | ����Ҫ�¨2�ѧϰ������ | 大部分字符为小方块 | 以 UTF-8 的方式读取 GBK 编码的中文 |
符号码 | 由月è|好好å-|ä1 天天å‘上 | 大部分字符为各种符号 | 以 ISO8859-1 方式读取 UTF-8 编码的中文 |
拼音码 | óéÔÂòaoÃoÃѧϰììììÏòéÏ | 大部分字符为头顶带有各种类似声调符号的字母 | 以 ISO8859-1 方式读取 GBK 编码的中文 |
问句码 | 由月要好好学习天天向?? | 字符串长度为偶数时正确,长度为奇数时最后的字符变为问号 | 以 GBK 方式读取 UTF-8 编码的中文,然后又用 UTF-8 的格式再次读取 |
锟拷码 | 锟斤拷锟斤拷要锟矫猴拷学习锟斤拷锟斤拷锟斤拷 | 全中文字符,且大部分字符为“锟斤拷”这几个字符 | 以 UTF-8 方式读取 GBK 编码的中文,然后又用 GBK 的格式再次读取 |
烫烫烫 | 烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫 | 字符显示为“烫烫烫”这几个字符 | VC Debug 模式下,栈内存未初始化 |
屯屯屯 | 屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯 | 字符显示为“屯屯屯”这几个字符 | VC Debug 模式下,堆内存未初始化 |
推荐插件 File Encoding Converter 安装完之后右键直接转换好使的一笔。 #TODO 字符集
时间格式和时区
Last Edit: 2025-09-16 00:06:52