Skip to main content

编码

Utf-8

是一种编码方式,可以实现对unicode和ASSIC进行编码

总结:

Unicode 提供了一个全球字符的统一标准。

ASCII 仅是一个较为古老的字符编码,支持有限的字符集,主要用于英语。

UTF-8 是一种 变长 字符编码方案,用来将 Unicode 代码点转换为字节流,并且与 ASCII 兼容

所以,如果一个字符编码成3个字节,但是一个字节数组的结尾可能只包含了其中两个字节,而后一个字节数组开头包含了该字符编码的最后一个字节,那么,如果两个字节数组单独解码,就会发生乱码。

要解决这个问题,要了解UTF-8的编码规则,如下所示:

1字节0xxxxxxx 2字节110xxxxx 10xxxxxx 3字节1110xxxx 10xxxxxx 10xxxxxx 4字节11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 5字节111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 6字节1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

因此UTF-8中可以用来表示字符编码最大是6个字节,实际位数最多有31位,即上表中x所表示的位。除去那些控制位(每字节开头的10等),这些x表示的位与UNICODE编码是一一对应的,位高低顺序也相同。

举个例子:

e4bda0e5a5bd0a 这个字节流每2为表示一个16进制,那就是 e4bda0 e5a5bd 0a

e4bda0 使用二进制表示,因为是汉字,因为使用了3个字符进行了编码

e4bda0 用二进制表示
e4 的二进制表示为:11100100
bd 的二进制表示为:10111101
a0 的二进制表示为:10100000

解码utf-8
这是一个 3 字节的 UTF-8 编码字符,符合 1110xxxx 10xxxxxx 10xxxxxx 的格式。
高 4 位: 从 e4 提取 0100
中间 6 位: 从 bd 提取 111101
低 6 位: 从 a0 提取 100000


然后进行拼凑后
0100 111101 100000 相当于 01001111 01100000
01001111 转换为16进制就是4F
01100000 转换为16进制就是60

即 4F60 4F60,这个 Unicode 代码点是 U+4F60,对应的字符是 "你"。
image-20250119153328444

同样的,剩余的e5a5bd表示"好",0a表示换行符

0a 转换为二进制
00001010

解码utf-8
这是一个 单 字节的 UTF-8 编码字符,符合 0xxxxxxx 的格式。
0000 1010

然后进行拼凑后
0000 1010 相当于 00001010
0000 转换为16进制就0
1010 转换为16进制就是a

转换为Unicode 代码点为: U+000a

注意:

Unicode 代码点 是一个 最大 32 位 的整数值,可以表示从 U+0000U+10FFFF 的字符

Unicode和ASSIC

ASCII(美国信息交换标准代码) 是最早的字符编码标准之一,它使用 7 位 来表示字符,支持 128 个字符。其中包含了英文字母(大小写)、数字、标点符号以及一些控制字符(如换行符、回车符等)。

  • 例如:
    • A 在 ASCII 中的代码是 0x41(十六进制)。
    • a 在 ASCII 中的代码是 0x61(十六进制)。
    • 换行符 \n 在 ASCII 中的代码是 0x0A

Unicode 是一个统一的字符集标准,旨在为全球所有书写系统的字符提供唯一的代码点。Unicode 支持全球几乎所有语言的字符,覆盖了超过 150,000 个字符(截至 Unicode 13.0)。

  • 例如:
    • A 在 Unicode 中的代码点是 U+0041(十六进制)
    • 在 Unicode 中的代码点是 U+4F60(十六进制)

ASCII 是 Unicode 的子集 Unicode 兼容 ASCII,意味着 Unicode 中的前 128 个代码点(即从 U+0000U+007F)与 ASCII 完全相同。所以,所有的 ASCII 字符(如字母、数字、标点符号)都可以在 Unicode 中表示,且代码点是一样的。

字节流:

抓包时获取的字节流地址如下: 字节流 e4bda0e5a5bd0a 解码后的字符是 "你好\n",其中包括了两个中文字符和一个换行符。

使用16进制表示,它将每个字节以 2 位十六进制数字来表示

文件操作:文件在磁盘上的存储实际上是字节流,打开文件时,操作系统通过字节流读取文件内容,并将数据传输到程序进行处理。

网络协议:网络中传输的各种数据(如 HTTP 请求、TCP 数据包等)都是以字节流的形式发送的。无论是文本、图片还是音频,都是通过字节流进行传输的。

输入输出(I/O):在编程中,文件读写和网络数据的传输常常通过字节流进行。例如,使用 fread()fwrite() 函数进行文件操作,或者使用 recv()send() 函数进行网络数据传输时,底层传输的都是字节流。

字节流:e4bda0 e5a5bd 0a

  1. e4bda0 解码

    • e4 的二进制表示为:11100100
    • bd 的二进制表示为:10111101
    • a0 的二进制表示为:10100000

    这是一个 3 字节的 UTF-8 编码字符,符合 1110xxxx 10xxxxxx 10xxxxxx 的格式。

    • 高 4 位:从 e4 提取 0100
    • 中间 6 位:从 bd 提取 111101
    • 低 6 位:从 a0 提取 100000

    拼接起来:0100 111101 100000,即 4F60,这个 Unicode 代码点是 U+4F60,对应的字符是 "你"

  2. e5a5bd 解码

    • e5 的二进制表示为:11100101
    • a5 的二进制表示为:10100101
    • bd 的二进制表示为:10111101

    这是一个 3 字节的 UTF-8 编码字符,符合 1110xxxx 10xxxxxx 10xxxxxx 的格式。

    • 高 4 位:从 e5 提取 0101
    • 中间 6 位:从 a5 提取 001001
    • 低 6 位:从 bd 提取 111101

    拼接起来:0101 001001 111101,即 597D,这个 Unicode 代码点是 U+597D,对应的字符是 "好"

  3. 0a 解码

    • 0a 是一个单字节,表示 换行符 (LF),它的 Unicode 代码点是 U+000A

总结

将这三个部分拼接起来:

  • e4bda0 解码为字符 "你"
  • e5a5bd 解码为字符 "好"
  • 0a 解码为换行符。

因此,字节流 e4bda0e5a5bd0a 解码后的结果是:

你好

image-20250119150458444

三次握手

首先Client向服务器端发起请求,在发送数据之前会建立三次握手。

Client: 10.51.10.49

Server: 10.51.10.61

验证环境,这里使用nc,nc具有tcp功能,可以用来分析三次握手和数据输出的整个过程

server:
nc -l 55555
我是中国人我骄傲
我是中国人我骄傲
我爱你中国


Client:
~ > nc 10.51.10.61 55555
我是中国人我骄傲
我爱你中国
祝福2025年新年快乐

A-> B 客户端初始化一个Seq = x, 发送到服务端

B->A 服务端收到请求之后,会进行一个Ack(seq+1),既Ack=x+1。也会初始化一个server端的seq=y

A-> 客户端收到服务端的确认包和seq之后。则发送一个确认包Ack=y+1, seq=x+1

​ 这个过程跟发送数据的过程不一样,发送数据的过程在后端会进行说明

image-20250119233522270

整体三次握手流程如下:

image-20250120000651840

发送数据部分

image-20250120001911240

在TCP协议的数据传输中,从第170个包开始,客户端发送了一段长度为25字节的数据,使用了SEQ=1ACK=1,表示这是一个正常的连接初始化数据包。由于网络问题,第174行发生了重传,在第175行,服务器成功接收到数据,并回复客户端确认接收到数据,发送了SEQ=1ACK=26的包。由于这是一个确认应答包,因此没有附带数据。

此时,SEQ=1 表示服务器在回复确认时的序列号,即它上一次接收数据包的序列号。ACK=26 则表示确认号,即确认客户端已成功发送的数据的最后一个字节的下一个序列号。这一过程表明,TCP协议通过确认机制确保数据传输的可靠性,只有在接收到数据并成功确认后,才会发送确认包。通过这一可靠的数据确认与重传机制,TCP保证了数据传输的完整性和准确性。

image-20250120003334124

同样的,在下面的281行,也进行了数据包的发送,seq=26,则表示上一次传输完数据包之后的序列号,数据长度为16字节,随机Ack=1,则在283行,则进行回复,Ack为(seq+16),seq=1 (既上次的Ack)。所以,这里也能够看到,每次发送数据时,都会生成一个随机的Ack(默认为1),直到此次任务结束。

发送大数据时的交互过程(序列号和确认号的工作)

假设客户端要向服务器发送 1MB 的数据,数据的大小远超单个 TCP 数据段的最大传输单元(MSS,通常为 1460 字节)。因此,数据将被分割成多个段进行传输。每个段都将带有一个序列号,接收方每次收到一个段后都会返回一个确认号。

步骤: 客户端发送数据

假设客户端的数据大小为 1MB,需要分割成多个数据段进行传输。假设每个数据段的大小是 1460 字节(即最大传输单元,MSS),所以客户端将数据分割成多个数据段(约 700 个段)。

  • 第一个数据段:
    • 序列号 SEQ = 1000(假设初始序列号为 1000)
    • 数据段的大小为 1460 字节,所以下一个数据段的序列号将是 SEQ = 1000 + 1460 = 2460
  • 第二个数据段:
    • 序列号 SEQ = 2460
    • 数据段大小为 1460 字节,所以下一个数据段的序列号将是 SEQ = 2460 + 1460 = 3920

这样,数据被分割成多个段,每个段的序列号按顺序递增,直到所有数据发送完。