Python 网络编程(进阶):TCP协议、TCP编程

  • 1. 预备知识
  • 分包
  • 粘包
  • 2. TCP协议
  • 2.1 TCP协议的作用
  • 分包:TCP传输的“特性”
  • 2.2 TCP 数据包的大小
  • 2.3 TCP数据包的编号(SEQ)
  • 2.4 TCP数据包的组装
  • 2.5 慢启动和 ACK
  • 2.6 数据包的遗失处理
  • TCP粘包与分包的解决
  • 分包与粘包
  • 简单的解决方案
  • 代码


1. 预备知识

分包

(1)what is 分包
TCP是 段(Segment)为单位 发送数据的,建立TCP链接后,有一个 最大报文长度(Maximum Segment Size,MSS) ,就是 TCP数据包每次能够传输的最大数据分段

(2)why use 分包
路由器有一个最大传输单元( Maximum Transmission Unit,MTU),一般是1500字节,除去IP头部20字节,留给TCP的就只有MTU-20字节。所以一般TCP的最大报文长度(MSS)为1460字节(MTU-20)。

最大传输单元(Maximum Transmission Unit,MTU)用来通知对方所能接受数据服务单元的最大尺寸,说明发送方能够接受的有效载荷大小。

粘包

**(1) what **

什么是“粘包”?

发送方发送两个字符串”hello”+”world”,接收方却一次性接收到了”helloworld”

(2) why

为什么出现粘包?

因为TCP socket通信面向流的传输协议,而UDP面向消息的传输。

所以在TCP socket中,当发送速度特别快,而接收端接收速度跟不上时,就会出现粘包的问题,即接收端一次读出多包数据或者读出两包数据的个一部分。而UDP不会出现这种情况

所以,TCP socket需要解决的最大问题可能是粘包问题

有时候,TCP为了提高网络的利用率,会使用一个叫做Nagle的算法。该算法是指,发送端即使有要发送的数据,如果很少的话,会延迟发送。如果应用层给TCP传送数据很快的话,就会把两个应用层数据包**“粘”在一起**,TCP最后只发一个TCP数据包给接收端。

2. TCP协议

2.1 TCP协议的作用

python解析dump蓝屏文件 python解析tcpdump_分包


TCP 是以太网协议和 IP 协议的上层协议,也是应用层协议的下层协议。

最底层的以太网协议(Ethernet)规定了电子信号如何组成数据包(packet),解决了子网内部的点对点通信。

但是,以太网协议不能解决多个局域网如何互通,这由 IP 协议解决。

IP 协议定义了一套自己的地址规则,称为 IP 地址。它实现了路由功能,允许某个局域网的 A 主机,向另一个局域网的 B 主机发送消息。
(路由器就是基于 IP 协议。局域网之间要靠路由器连接。)

IP 协议只是一个地址协议,并不保证数据包的完整。如果路由器丢包(比如缓存满了,新进来的数据包就会丢失),就需要发现丢了哪一个包,以及如何重新发送这个包。这就要依靠 TCP 协议

分包:TCP传输的“特性”

2.2 TCP 数据包的大小

一图胜千言:

python解析dump蓝屏文件 python解析tcpdump_TCP_02


概述:

  • 以太网数据包(packet)大小 = 1522字节(最新)= 负载(payload)1500字节 + 头信息(head)22字节
  • 以太网报头至少 22 字节
  • IP报头和TCP报头至少 20字节
  • TCP 数据包的最大负载是 1480 - 20 = 1460 字节
  • IP 和 TCP 协议往往有额外的头信息,所以 TCP 负载实际为1400字节左右

因此,一条1500字节的信息需要两个 TCP 数据包。
HTTP/2 协议的一大改进, 就是压缩 HTTP 协议的头信息,使得一个 HTTP 请求可以放在一个 TCP 数据包里面,而不是分成多个,这样就提高了速度。

2.3 TCP数据包的编号(SEQ)

一个包1400字节,那么一次性发送大量数据,就必须分成多个包。比如,一个 10MB 的文件,需要发送7100多个包。

发送的时候,TCP 协议为每个包编号(sequence number,简称 SEQ),以便接收的一方按照顺序还原。万一发生丢包,也可以知道丢失的是哪一个包。

第一个包的编号是一个随机数。
为了便于理解,这里就把它称为1号包。假定这个包的负载长度是100字节,那么可以推算出下一个包的编号应该是101
这就是说,每个数据包都可以得到两个编号:自身的编号,以及下一个包的编号。接收方由此知道,应该按照什么顺序将它们还原成原始文件。

2.4 TCP数据包的组装

收到 TCP 数据包以后,组装还原操作系统完成的。应用程序不会直接处理 TCP 数据包。

简述这个过程中各个部分所起的作用:

  • TCP协议:给数据包编号(①便于文件还原:按编号顺序 ②便于确定丢失的包进行重发)
  • 操作系统:组装 TCP数据包里的数据(不包括处理)
  • 应用程序:接收操作系统组装好的TCP数据包
    (TCP 数据包里面有一个端口(port)参数,就是用来指定转交给监听该端口的应用程序。)
  • 应用层协议(比如 HTTP协议):表示原始文件的大小,并在应用程序接收组装好的数据后根据协议指定的Content-Length(HTTP协议)正确地读出一段段数据。
    (这也意味着,一次 TCP 通信可以包括多个 HTTP 通信)

##------- 丢包:TCP传输的问题---------##

2.5 慢启动和 ACK

TCP 协议为了做到效率与可靠性的统一,设计了一个慢启动(slow start)机制
开始的时候,发送得较慢,然后根据丢包的情况,调整速率:如果不丢包,就加快发送速度;如果丢包,就降低发送速度。

默认情况下,接收方每收到两个 TCP 数据包,就要发送一个确认消息。"确认"的英语是 acknowledgement,所以这个确认消息就简称 ACK

ACK 携带两个信息。

  • 期待要收到下一个数据包的编号
  • 接收方的接收窗口的剩余容量

发送方有了这两个信息,再加上自己已经发出的数据包的最新编号,就会推测出接收方大概的接收速度,从而降低或增加发送速率。这被称为 “发送窗口”,这个窗口的大小是可变的。

2.6 数据包的遗失处理

TCP 协议可以保证数据通信的完整性,这是怎么做到的?

每一个数据包都带有下一个数据包的编号。
如果下一个数据包没有收到,那么 ACK 的编号就不会发生变化。
如果发送方发现收到三个连续的重复 ACK,或者超时了还没有收到任何 ACK,就会确认丢包,从而再次发送这个包。通过这种机制,TCP 保证了不会有数据包丢失。

TCP粘包与分包的解决

分包与粘包

(1)分包
一个以太网包只能传输1500字节长度的数据,而在这其中,IP头、TCP头又各占了20个字节,因此有效载荷为1460字节。
如果要发送的数据的长度超过了1460,假设为2000,那么必然被分成多个以太网包发送过来。
对于接收方来说,如果每次接收1024个字节,则需要多次recv才能把这整段数据接收,而TCP是不维护数据边界的,因此对于接收方来说,完全不知道这一段数据什么时候结束。
(2)粘包
粘包和分包相反,要发送的数据长度很短,比如只有30个字节左右,如果以非常快的速度发送,那么有可能一个以太网包里包含了好几段数据,他们是被一起发送过来的,这时接收方recv得到的数据是好几段数据连在一起,无法分开。

简单的解决方案

(1)方案一
如何解决呢,一种方法就是约定好命令的长度,这样一来,接收方就可以根据提前约定好的数据长度来解析数据了。但这样会产生许多不必要的麻烦了,比如实际发送数据小于约定长度时需要填充,这样也造成了传输上的浪费。
(2)方案二
另一种方法就是对要传输的数据进行封装,比如在数据的最前面加上一个长度标识,指明本次要发送的数据长度是多少,这样一来,接收方先获得数据的长度,然后根据数据的长度来获取实际数据。

也有人这样做:
因此如果要使用socket通信,就一定要自己定义一份协议(在应用层下)。目前最常用的协议标准是:消息头部(包头)+ 消息长度 + 消息正文。