TCP协议概览
2022-04-02 # 学习笔记 # 计算机网络

TCP所面临的问题及解决方案

问题:

下层IP的交付为面向无连接和不可靠的协议,而TCP要向应用层提供面向连接的可靠的协议。

同时TCP要实现流量控制和拥塞控制

什么是面向连接:

需要确定传输的对方处于等待接受和发送的状态上,同时也需要维护传输数据的关系,例如数据流的顺序。其实也就是要确保对方能准确的收到原模原样的数据。

以实际例子类比,面向连接更像是打电话,而无连接则更像是发短信。

如何解决:

如何解决面向连接

解决确定传输对方的状态问题:在包内加上特殊的标记,用来专门创建断开和维护一个连接:syn,ack,fin,rst

解决数据顺序和重复问题:维护seq序列号字段

如何解决可靠性:

引入数据传输确认机制:维护确认字段Acknowledgement和ack状态,也就是停止等待协议。

但停止等待协议导致了带宽利用率不高的问题

如何解决带宽利用率不高:

引入了窗口确认机制和滑动窗口:可以在发送多个包之后一起确认

如何实现拥塞控制

引入了拥塞判断机制和拥塞窗口

TCP状态机——三次握手四次挥手

三次握手

为什么要三次?

从TCP要实现面向连接的目的来看,目的主要有两个:

1.确认对端在线,即一端请求另一端能立刻给出响应。也可以说是确认双方同时都有对数据接受和发送的能力

2.面向连接的传输需要保证包的次序,因此两端都需要确认对方的起始序列号。从而对接受来的数据排序。

为什么两次不行:两次握手至少让一端无法确认对方是否了解自己的起始序列号。

eNSP模拟

在ensp中模拟如下的环境,并进行抓包分析。

可以很明显的看到三次握手

四次挥手

为什么是四次挥手,而不是两次?

如果对于单工通信来说,A发送,B接受,A向B发送结束的通知,B向A回复收到。这样拆除连接是完全可行的。

但对于全双工通信来说,一个方向的传输关闭之后,另一个方向的传输也有可能还在继续。两个方向需要分别关闭才可以。

类比一下(四次挥手):

A:我这边没数据传啦!

B:好的。

(可能B向A继续传了一些没传完的数据)

B:我这边的数据也传完了!

A:了解!

和上面握手的时候同样的环境进行抓包,很容易的捕捉到了四次挥手的过程。

如果A要切断自己发送的连接的时候,B也没有数据要发了,能合并成三次吗?

答:可以

可以看到再下图中挥手次数只有三次。其中中间的两次出现了合并的现象。

这是因为。TCP有Delay ACK的机制

阅读参考:https://blog.csdn.net/dog250/article/details/52664508

类比一下(三次挥手):

A:我这边没数据传啦!

B:好的。我这边的数据也传完了!

A:了解!

在中间有数据要传的情况下也能合并吗?

答:可以

类比一下(三次挥手带数据):

A:我这边没数据传啦!

B:好的。我这边的数据也只剩这最后一点了!(data)

A:了解,那我就关掉连接了!

各种状态以及可能引起的问题分析

搬运自:https://zorrozou.github.io/docs/tcp/wavehand/TCP_Wavehand.html

为什么要有TimeWait状态

要确保最后一个ACK送达。

如果最后一个ACK未送达。那么服务器会重发FIN,这个时候如果客户端已经关闭了连接。那么就会导致服务器一直超时重发无应答。

所以客户端维护一段时间的TimeWait状态。RFC规定要等待两倍的MSL。RFC规定要等待两倍的MSL(一个数据分片(报文)在网络中能够生存的最长时间),这个时间也被RFC规定为2分钟。但是实际Linux的实现并不用等这么久,Linux的实现中,TCP_TIME_WAIT等待时间被固定设置为60秒。

FIN_WAIT1驻留问题

主动关闭方发出fin之后会进入到FIN_WAIT1状态,这个状态会持续到对方回复ack为止。一般情况下,这个状态都不会持续太久,大概在一个rtt左右。但是在某些异常情况下,它可能会长时间存在于服务器上。如果出现这种情况,主要原因应该是对端行为异常,要么对端出现bug,要么可能是故意发起的攻击行为。在这种情况下,对应用程序进行重启一般不会缓解现象,因为此时应用程序已经认为TCP连接被关闭,这种TCP连接已经是单纯的内核行为,并不关联任何应用程序。我们把这种应用程序称为tcp orphans(tcp孤儿)。内核维护这种TCP状态需要消耗内存,所以如果这种连接多了会产生针对内存的DoS(拒绝服务)攻击。

FIN_WAIT2驻留问题

参考:https://blog.csdn.net/dog250/article/details/81256550

CLOSE_WAIT

正常情况下,被动关闭方接受到对端发来的fin之后,会立即发送ack,并进入CLOSE_WAIT状态。直到确认本端也没数据要发了,给对端发送fin之前,会一直维持这个状态。

大量遗留的CLOSE_WAIT状态是在业务上经常出现的问题,尤其是对于很多自己研发的软件来说尤其是如此。我们可以想像,什么情况下会有大量CLOSE_WAIT状态遗留?那就是tcp通知应用层说对方已经关闭了,你还有数据要发么?而应用层怎么样?压根没反应。所以,一般这种异常都是被动关闭方没有正确处理来自socket的事件而产生的问题。遇到这种问题是,请检查代码bug。

LAST_LCK

当被动关闭方发出要跟对方关闭连接的fin之后,本端会进入LAST_ACK状态,并持续到对端发来ack为止。那么如果对方没有发来ack,本端当重传次数超限后,会关闭连接。

一些搬运问题

搬运来源:https://zhuanlan.zhihu.com/p/86426969

1.ISN(Initial Sequence Number)是固定的吗?

三次握手的其中一个重要功能是客户端和服务端交换 ISN(Initial Sequence Number),以便让对方知道接下来接收数据的时候如何按序列号组装数据。如果 ISN 是固定的,攻击者很容易猜出后续的确认号,因此 ISN 是动态生成的。

2.三次握手过程中可以携带数据吗?

​ 其实第三次握手的时候,是可以携带数据的。但是,第一次、第二次握手不可以携带数据。

​ 假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据。因为攻击者根本就不理服务器的接收、发送能力是否正常,然后疯狂着重复发 SYN 报文的话,这会让服务器花费很多时间、内存空间来接收这些报文。

​ 也就是说,第一次握手不可以放数据,其中一个简单的原因就是会让服务器更加容易受到攻击了。而对于第三次的话,此时客户端已经处于 ESTABLISHED 状态。对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以能携带数据也没啥毛病。

3.SYN攻击是什么?

服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,所以服务器容易受到SYN洪泛攻击。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server则回复确认包,并等待Client确认,由于源地址不存在,因此Server需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪。SYN 攻击是一种典型的 DoS/DDoS 攻击。

检测 SYN 攻击非常的方便,当你在服务器上看到大量的半连接状态时,特别是源IP地址是随机的,基本上可以断定这是一次SYN攻击。在 Linux/Unix 上可以使用系统自带的 netstats 命令来检测 SYN 攻击。

1
netstat -n -p TCP | grep SYN_RECV

常见的防御 SYN 攻击的方法有如下几种:

  • 缩短超时(SYN Timeout)时间
  • 增加最大半连接数
  • 过滤网关防护
  • SYN cookies技术