编码
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,对应的字符是 "你"。
同样的,剩余的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+0000 到 U+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+0000 到 U+007F)与 ASCII 完全相同。所以,所有的 ASCII 字符(如字母、数字、标点符号)都可以在 Unicode 中表示,且代码点是一样的。
字节流:
抓包时获取的字节流地址如下:
字节流 e4bda0e5a5bd0a 解码后的字符是 "你好\n",其中包括了两个中文字符和一个换行符。
使用16进制表示,它将每个字节以 2 位十六进制数字来表示
文件操作:文件在磁盘上的存储实际上是字节流,打开文件时,操作系统通过字 节流读取文件内容,并将数据传输到程序进行处理。
网络协议:网络中传输的各种数据(如 HTTP 请求、TCP 数据包等)都是以字节流的形式发送的。无论是文本、图片还是音频,都是通过字节流进行传输的。
输入输出(I/O):在编程中,文件读写和网络数据的传输常常通过字节流进行。例如,使用 fread() 或 fwrite() 函数进行文件操作,或者使用 recv() 和 send() 函数进行网络数据传输时,底层传输的都是字节流。
字节流:e4bda0 e5a5bd 0a
-
e4bda0解码e4的二进制表示为:11100100bd的二进制表示为:10111101a0的二进制表示为:10100000
这是一个 3 字节的 UTF-8 编码字符,符合
1110xxxx 10xxxxxx 10xxxxxx的格式。- 高 4 位:从
e4提取0100 - 中间 6 位:从
bd提取111101 - 低 6 位:从
a0提取100000
拼接起来:
0100 111101 100000,即4F60,这个 Unicode 代码点是U+4F60,对应的字符是 "你"。 -
e5a5bd解码e5的二进制表示为:11100101a5的二进制表示为:10100101bd的二进制表示为:10111101
这是一个 3 字节的 UTF-8 编码字符,符合
1110xxxx 10xxxxxx 10xxxxxx的格式。- 高 4 位:从
e5提取0101 - 中间 6 位:从
a5提取001001 - 低 6 位:从
bd提取111101
拼接起来:
0101 001001 111101,即597D,这个 Unicode 代码点是U+597D,对应的字符是 "好"。 -
0a解码0a是一个单字节,表示 换行符 (LF),它的 Unicode 代码点是U+000A。
总结
将这三个部分拼接起来:
e4bda0解码为字符 "你"。e5a5bd解码为字符 "好"。0a解码为换行符。
因此,字节流 e4bda0e5a5bd0a 解码后的结果是:
你好

三次握手
首先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
这个过程跟发送数据的过程不一样,发送数据的过程在后端会进行说明

整体三次握手流程如下:
发送数据部分
在TCP协议的数据传输中,从第170个包开始,客户端发送了一段长度为25字节的数据,使用了SEQ=1 和 ACK=1,表示这是一个正常的连接初始化数据包。由于网络问题,第174行发生了重传,在第175行,服务器成功接收到数据,并回复客户端确认接收到数据,发送了SEQ=1和ACK=26的包。由于这是一个确认应答包,因此没有附带数据。
此时,SEQ=1 表示服务器在回复确认时的序列号,即它上一次接收数据包的序列号。ACK=26 则表示确认号,即确认客户端已成功发送的数据的最后一个字节的下一个序列号。这一过程表明,TCP协议通过确认机制确保数据传输的可靠性,只有在接收到数据并成功确认后,才会发送确认包。通过这一可靠的数据确认与重传机制,TCP保证了数据传输的完整性和准确性。

同样的,在下面的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
- 序列号
这样,数据被分割成多个段,每个段的序列号按顺序递增,直到所有数据发送完。