在面试中但凡问到计网,肯定会问TCP相关的问题,而很多细节总是遗忘,所以需要专门以问答的方式写一篇博客来记载一下。

TCP三次握手的目的是什么?为什么不用两次和四次?

TCP三次握手的主要目的是防止失效的连接请求报文被服务端接受

如果只有两次握手,假设当客户端发送第一次连接请求由于网络拥塞的原因,迟迟未到服务端,客户端没接收到确认报文,认为服务端没有收到,于是重新发送请求报文并与服务端建立连接,等这次连接断开了,之前滞留的那个请求报文又到达了服务端,就会让服务端与客户端再次连接成功,这时服务端就会一直等待客户端发送请求,造成了资源的浪费。

两次握手只能保证单向链路是可以通信的,理论上来说,要保证双向链路可以通信需要四次握手,但实际上服务端给客户端的SYN和ACK数据包可以合为一次握手,所以实际上只需要三次握手即可。

  • 加问:那挥手为什么需要四次呢?三次不行吗?
  • 答:挥手阶段中服务端的ACK和FIN数据包不能合为一次。因为挥手阶段的流程为客户端发送FIN数据包表示自己发完了,服务端立即回复ACK数据包表示自己知道了,此时客户端到服务端的连接已经释放了,客户端不会再发送数据了,但服务端还可以继续向客户端发送数据,等到服务端也完成了数据发送,才会发送FIN,这时客户端回复ACK,就可以结束通信了。
  • 加问:TCP在四次挥手的过程中为什么客户端最后还要等待2MSL(Maximum Segment Lifetime)?
  • 答:因为客户端要保证他的ACK包顺利到达服务端,如果客户端的ACK数据包丢失,则服务端或重新发送FIN包到客户端,而这两个过程的最长时间为1MSL,加起来为2MSL,如果2MSL后客户端还没有收到服务端重发的FIN包,则说明ACK包顺利到达,可以关闭连接了。

TCP在握手阶段怎么管理客户端的连接?

TCP在握手阶段服务端维护了两个队列:半连接队列和全连接队列

  • 在客户端发起第一次握手时,服务端会把此请求放入半连接队列,并回复SYN+ACK
  • 在客户端回复ACK,也就是第三次握手时,服务端将此连接加入到全连接队列
  • 如果全连接队列满,则服务端的处理方式和tcp_abort_on_overflow参数的设置有关,如果该参数为0,则丢弃该ACK,如果为1则发送RST到客户端,直接放弃此次连接。

此条是我在了解DDOS时发现的,并非常考点,SYN Flood攻击时会造成服务端的半连接队列被占满,从而影响到服务。

TCP通过哪些方式来保证数据的可靠性?

TCP保证数据可靠性的方式大致可以分为三类:

  • 在数据包层面:校验和
  • 在数据包传输层面:序列号、确认应答、超时重传
  • 在流量控制层面:拥塞控制

校验和

计算方式:在数据传输的过程中,将发送的数据段都当做一个16位的整数。将这些整数加起来。并且加上进位,最后取反,得到校验和。
TCP与UDP校验方式相同

序列号、确认应答、超时重传

在数据包传输的过程中,每个数据包都有一个序列号,当数据到达接收方时,接收方会发出一个确认应答,表示收到该数据包,并会说明下一次需要接收到的数据包序列号(32位确认序列号)。如果发送端在一段时间内(2RTT没有收到确认应答,则说明可能是发送的数据包丢失或者确认应答包丢失,此时发送端会进行数据包重传。

但发送端并不是一定要等到接收到上一个数据包的确认应答再发送下一个数据包,TCP会利用窗口控制来提高传输速度,在一个发送窗口大小内,不用一定要等到应答才能发送下一段数据,发送窗口大小就是无需等待确认而可以继续发送数据的最大值。而发送窗口的大小是由接收端的接受窗口的剩余大小和拥塞窗口来决定的。(TCP会话的双方都各自维护一个发送窗口和一个接收窗口)

拥塞控制

发送端维持一个叫做拥塞窗口cwnd(congestion window)的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。发送端让自己的发送窗口等于拥塞窗口,另外考虑到接受方的接收能力,发送窗口可能小于拥塞窗口。

TCP的拥塞控制主要是采用慢启动以及增性加,乘性减的机制,TCP一开始将拥塞窗口设置的很小,在逐渐经过一段时间的指数增长后超过门限,进入增性加阶段,此时窗口大小的增长是线性的,比之前的指数增长要慢很多,而当发生网络拥塞时,拥塞窗口大小直接减半(乘性减)。

TCP长连接和短连接有什么区别?

TCP短连接是指客户端与服务端连接后只进行一次读写就关闭连接,一般是客户端关闭。
而长连接则是指在进行完一次读写后不关闭连接,直到服务端压力过大则选择关闭一些长时间为进行读写的连接。

TCP短连接的优点在于管理简单,而且不会对服务端造成太大的压力,而缺点是每次读写都需要连接耗时较长。

TCP长连接的优点是可以迅速进行多次读写,缺点是对服务端压力大,且容易被恶意连接影响服务。

长短连接的区别就在于客户端和服务端选择的关闭策略不同,具体需要根据应用场景来选择合适的策略。

TCP粘包、拆包及解决方法?

TCP之所以会产生粘包和拆包拆包问题,是因为他本身就是一种字节流协议,TCP本身就没有数据包的概念,需要发送和接受的数据是没有格式的,以字节流的形式传输,而在传输过程中会被分割为一段段数据块,也就是报文。TCP要发送的数据会被先放置在数据缓冲区,接收数据也是从缓冲区获取,而缓冲区的大小即为最大报文长度,如果需要发送的数据长度大于缓冲区剩余的大小或者大于最大报文长度,则会出现拆包,如果是需要发送的数据很少,而短时间内又有其他数据包需要发送,就会出现粘包的现象。

解决方案有很多种,可以在数据包头加上数据包长度,或者把每个数据包封装为固定长度,不够则补0,以及可以使用特定分割符号等等

我们在项目中也遇到过这种问题,因为我们在做流量检测的时候,有时候难以找到恶意软件的流量特征,会把数据包长度当做特征来使用,有些恶意软件内部无论会把这些数据包长度写死,这样恶意软件本身就不存在有无法解析粘包和拆包的情况,但对于我们来说,检测就会遇到障碍,尤其是攻击者可以设置MSS来使得数据包长度改变,对于这种攻击我们目前也没有很好的方案来解决。

TCP进阶问题

不会吧不会吧,真的有人会问的这么难嘛

参考TCP细节知识

参考链接