声明:本文主要探讨当TCP协议出现在面试笔试场合可能会涉及的问题,每一个知识点讨论力求简洁,便于记忆,但讨论深度有限,如要深入研究可点击参考链接,希望对正在找工作的同学有点帮助。
一、TCP协议简介
一般问到TCP协议的时候 最常见的是TCP连接建立和断开的过程,也就是三次握手和四次挥手,两张图足矣。
1.1 三次握手
1.2 四次挥手
二、常见面试题
2.1 TCP连接阶段
2.1.1 发送序号和确认序号问题
例: TCP建立连接的过程采用三次握手,已知第三次握手报文的发送序列号为1000,确认序列号为2000,请问第二次握手报文的发送序列号和确认序列号分别为?
答:看答案时请参考上面TCP连接建立的图。
客户端:发送X
服务端:发送Y, 确认X+1
客户端:发送X+1(1000),确认Y+1(2000)
可以反推第二次为1999,确认1000
2.1.2 SYN Flood 攻击原理及防御
SYN Flood是当前最流行的DoS(拒绝服务攻击)与DDoS(分布式拒绝服务攻击)的方式之一,这是一种利用TCP协议缺陷,发送大量伪造的TCP连接请求,从而使得被攻击方资源耗尽(CPU满负荷或内存不足)的攻击方式。
原理:
问题出在TCP连接的三次握手中,恶意的攻击者大量发送SYN报文,服务器端将为了维护一个非常大的半连接列表而消耗非常多的资源----数以万计的半连接,即使是简单的保存并遍历也会消耗非常多的CPU时间和内存,何况还要不断对这个列表中的IP进行SYN+ACK的重试。实际上如果服务器的TCP/IP栈不够强大,最后的结果往往是堆栈溢出崩溃---即使服务器端的系统足够强大,服务器端也将忙于处理攻击者伪造的TCP连接请求而无暇理睬客户的正常请求(毕竟客户端的正常请求比率非常之小),此时从正常客户的角度看来,服务器失去响应,这种情况我们称作:服务器端受到了SYN Flood攻击(SYN洪水攻击)。
攻击方式:
- Direct Attack 攻击方使用固定的源地址发起攻击,这种方法对攻击方的消耗最小
- Spoofing Attack 攻击方使用变化的源地址发起攻击,这种方法需要攻击方不停地修改源地址,实际上消耗也不大
- Distributed Direct Attack 这种攻击主要是使用僵尸网络进行固定源地址的攻击
防御方法:
- 无效连接监视释放
不停监视系统的半开连接和不活动连接,当达到一定阈值时拆除这些连接,从而释放系统资源。 - 延缓TCB分配方法
从前面SYN Flood原理可以看到,消耗服务器资源主要是因为当SYN数据报文一到达,系统立即分配TCB,从而占用了资源。而SYN Flood由于很难建立起正常连接,因此,当正常连接建立起来后再分配TCB则可以有效地减轻服务器资源的消耗。常见的方法是使用Syn Cache和Syn Cookie技术。
Syn Cache技术:
这种技术是在收到SYN数据报文时不急于去分配TCB,而是先回应一个SYN ACK报文,并在一个专用HASH表(Cache)中保存这种半开连接信息,直到收到正确的回应ACK报文再分配TCB。
Syn Cookie技术:
Syn Cookie技术则完全不使用任何存储资源,它使用一种特殊的算法生成Sequence Number,这种算法考虑到了对方的IP、端口、己方IP、端口的固定信息,以及对方无法知道而己方比较固定的一些信息,如MSS、时间等,在收到对方的ACK报文后,重新计算一遍,看其是否与对方回应报文中的(Sequence Number-1)相同,从而决定是否分配TCB资源。 - 使用SYN Proxy防火墙
防火墙中提供一种SYN代理的功能,主要原理是对试图穿越的SYN请求进行验证后才放行。
详情请移步 SYN Flood攻击及防御方法
2.1.3 为什么TCP建立连接要三次握手
首先得回答三次握手的目的是同步连接双方的序列号和确认号并交换 TCP 窗口大小信息。
然后可以回答为什么两次握手不行,两次握手可能因为丢包而出现死锁,假设在两次握手场景中,C向S发送请求,S收到并发送确认请求给C,这时候S认为连接已经建立,并开始发送数据给C,但是那个确认请求丢包了,C不认为请求建立了,C当然会拒绝接受S发送来的数据,并且再去请求连接。这样,一个资源就死锁了。
最后回答握手当然可以四次五次一直握下去,但三次已经够了,就没有必要了。
总结下来一句话,主要目的防止在网络发生延迟或者丢包的情况下浪费资源。
详情请移步 TCP为什么要三次握手,不是两次四次
2.2 TCP传输阶段
2.2.1 滑动窗口以及拥塞控制
TCP协议作为一个可靠的面向流的传输协议,其可靠性和流量控制由滑动窗口协议保证,而拥塞控制则由控制窗口结合一系列的控制算法实现。
详情请移步 tcp窗口滑动以及拥塞控制
2.3 TCP断开阶段
2.3.1 设置TIME_WAIT的原因
- 可靠地实现TCP全双工连接的终止
TCP协议在关闭连接的四次握手过程中,最终的ACK是由主动关闭连接的一端(后面统称A端)发出的,如果这个ACK丢失,对方(后面统称B端)将重发出最终的FIN,因此A端必须维护状态信息(TIME_WAIT)允许它重发最终的ACK。如果A端不维持TIME_WAIT状态,而是处于CLOSED 状态,那么A端将响应RST分节,B端收到后将此分节解释成一个错误(在java中会抛出connection reset的SocketException)。
因而,要实现TCP全双工连接的正常终止,必须处理终止过程中四个分节任何一个分节的丢失情况,主动关闭连接的A端必须维持TIME_WAIT状态 - 允许老的重复分节在网络中消逝
TCP分节可能由于路由器异常而“迷途“,在迷途期间,TCP发送端可能因确认超时而重发这个分节,迷途的分节在路由器修复后也会被送到最终目的地,这个迟到的迷途分节到达时可能会引起问题。在关闭“前一个连接”之后,马上又重新建立起一个相同的IP和端口之间的“新连接”,“前一个连接”的迷途重复分组在“前一个连接”终止后到达,而被“新连接”收到了。为了避免这个情况,TCP协议不允许处于TIME_WAIT状态的连接启动一个新的可用连接,因为TIME_WAIT状态持续2MSL,就可以保证当成功建立一个新TCP连接的时候,来自旧连接重复分组已经在网络中消逝。
2.3.2 大量 TIME_WAIT 产生的原因及解决办法
原因:对于基于TCP的HTTP协议,关闭TCP连接的是Server端,这样,Server端会进入TIME_WAIT状态,可想而知,对于访问量大的Web Server,会存在大量的TIME_WAIT状态。
解决办法:
- 开启socket重用,允许将TIME_WAIT的socket重新用于TCP连接
- 开启快速回收。
2.3.3 大量CLOSE_WAIT产生的原因及解决办法
原因:对方关闭连接之后服务器程序自己没有进一步发出ack信号。换句话说,就是在对方连接 关闭之后,程序里 没有检测到,或者程序压根就忘记了这个时候需要关闭连接,于是这个资源就一直被程序占着。
解决办法:
具体问题具体解决,总结一句话就是在处理资源时一定要记住自己申请的资源要记得主动释放。
详情请移步 再谈应用环境下的TIME_WAIT和CLOSE_WAIT
2.3.4 为什么TCP断开连接要四次挥手
因为tcp是全双工模式,接收到FIN时意味将没有数据再发来,但是还是可以继续发送数据。
详情请移步 TCP为什么需要3次握手与4次挥手
2.4 其他
2.4.1 出现RST包的原因
RST:TCP首部中的6个标志比特之一,表示重置连接、复位连接。
- 服务器端口未打开而客户端来连接时
- 在一个已关闭的SOCKET上收到数据
- 请求超时
- 提前关闭
详细请移步 几种TCP连接中出现RST的情况