TCP为什么要给报文编号?不编号会有什么问题?
保证报文的不重复、不丢失、不乱序。
作为一个可靠的传输层协议,TCP 需要在不稳定的网络环境中构建一个可靠的传输层,网络的不确定性可能会导致数据包的缺失和顺序颠倒等问题,常见的问题可能包括:
- 数据包被发送方多次发送造成数据的重复;
- 数据包在传输的过程中被路由或者其他节点丢失;
- 数据包到达接收方可能无法按照发送顺序;
为了解决上述这些可能存在的问题,TCP 协议要求发送方在数据包中加入『序列号』字段,有了数据包对应的序列号,我们就可以:
- 接收方可以通过序列号对重复的数据包进行去重;
- 发送方会在对应数据包未被 ACK 时进行重复发送;
- 接收方可以根据数据包的序列号对它们进行重新排序;
序列号在 TCP 连接中有着非常重要的作用,初始序列号作为 TCP 连接的一部分也需要在三次握手期间进行初始化,由于 TCP 连接通信的双方都需要获得初始序列号,所以它们其实需要向对方发送 SYN 控制消息并携带自己期望的初始化序列号 SEQ,对方在收到 SYN 消息之后会通过 ACK 控制消息以及 SEQ+1 来进行确认。
为什么ACK报文中确认序号一定要是已经接收到的连续报文的最大的序号?改为已经接收到的不连续的报文的最大序号会有什么问题?
TCP把数据视为一个无结构但有序的字节流,序号建立在传输的字节流之上,而不建立在报文段之上。
接收端给发送端的Ack确认只会确认最后一个连续的包,比如,发送端发了1,2,3,4,5一共五份数据,接收端收到了1,2,于是回ack 3,然后收到了4(注意此时3没收到),此时的TCP会怎么办?我们要知道,因为正如前面所说的,SeqNum和Ack是以字节数为单位,所以ack的时候,不能跳着确认,只能确认最大的连续收到的包,不然,发送端就以为之前的都收到了。
因为TCP有窗口拥塞控制,窗口的拥塞是基于字节的,所以必须给字节编号,如果给数据包编号,拥塞窗口无法调节大小。
TCP三次握手中的初始序号是固定的吗?
不是固定的,是动态增长+随机选取,可以避免相邻的TCP会话的序列号有重叠,否则并不知道报文是旧连接的还是新连接的
如果初始序号是固定的,攻击者就可以很容易伪造TCP报文发起攻击,初始序号需要动态生成,提高攻击成本。
源IP地址、源端口、目标IP地址、目标端口唯一确定一个TCP会话,允许刚释放的TCP端口重用,如果旧会话中的分组报文仍然在网络中,新会话建立后可能会收到旧会话产生的分组报文,如果序号是一直递增的,就可以分辨出报文是属于旧会话的还是新会话的。
关于ISN的初始化。ISN是不能hard code的,不然会出问题的——比如:如果连接建好后始终用1来做ISN,如果client发了30个segment过去,但是网络断了,于是 client重连,又用了1做ISN,但是之前连接的那些包到了,于是就被当成了新连接的包,此时,client的Sequence Number 可能是3,而Server端认为client端的这个号是30了。全乱了。RFC793中说,ISN会和一个假的时钟绑在一起,这个时钟会在每4微秒对ISN做加一操作,直到超过2^32,又从0开始。这样,一个ISN的周期大约是4.55个小时。因为,我们假设我们的TCP Segment在网络上的存活时间不会超过Maximum Segment Lifetime(缩写为MSL – Wikipedia语条),所以,只要MSL的值小于4.55小时,那么,我们就不会重用到ISN。
报文编号和确认应答机制存在什么问题?
队头阻塞。
TCP数据包是有序传输,中间一个数据包丢失,会等待该数据包重传,造成后面的数据包的阻塞。