迎新真好笑,还是充实自己
多次在网上看见面试时问到的TCP三次握手问题,都是看了个大概罢了,这两天好好理解了一下

TCP的三次握手四次挥手

TCP(Transmission Control Protocol)全称:传输控制协议

TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。用户数据报协议(UDP)是同一层内另一个重要的传输协议.
TCP 要求在传送数据之前必须先建立连接,数据传送完成后要释放连接。因此TCP是一种可靠的的运输服务,但是正因为这样,不可避免的增加了许多的开销,比如确认,流量控制等。对应的应用层的协议主要有 SMTP,TELNET,HTTP,FTP 等.

TCP长啥样

注:主要是TCP报文头部
如下图:
TCP报头

其中:

  • 源端口和目的端口:各占2个字节(16位),分别写入源端口和目的端口.

  • 序号:占4个字节(32位),序号是本报文段发送的数据组的第一个字节的序号。TCP连接中传送的字节流中的每个字节都按顺序编号。例如,一段报文的序号字段值是 100 ,而携带的数据共有100字段,显然下一个报文段(如果还有的话)的数据序号应该从200开始。序号确保了TCP传输的有序性.

  • 确认号(即ACKnum):占4个字节(32位),指明下一个期待收到的字节序号,表明该序号之前的所有数据已经正确无误的收到。

    例如,B收到了A发送过来的报文,其序列号字段是501,而数据长度是200字节,这表明B正确地收到了A发送的直到序号700为止的数据。因此,B期望收到A的下一个数据序号是701,于是B在发送给A的确认报文段中把确认号置为701.
    确认号只有当ACK标志为1时才有效。比如建立连接时,SYN报文的ACK标志位为0.

  • 首部长度/数据偏移:占4位(32位),它指出TCP报文的数据距离TCP报文段的起始处有多远

    由于首部可能含有可选项内容,因此TCP报头的长度是不确定的,报头不包含任何任选字段则长度为20字节,4位首部长度字段所能表示的最大值为1111,转化为10进制为15,15*32/8 = 60,故报头最大长度为60字节。首部长度也叫数据偏移,是因为首部长度实际上指示了数据区在报文段中的起始偏移值.

  • 保留:占6位,保留今后使用,但目前应都位0.

  • 紧急URG:占2字节(16位),当URG=1,表明紧急指针字段有效。告诉系统此报文段中有紧急数据.

  • 确认ACK:占1位,仅当ACK=1时,确认号字段才有效。TCP规定,在连接建立后所有报文的传输都必须把ACK置1

  • 推送PSH:占1位,当两个应用进程进行交互式通信时,有时在一端的应用进程希望在键入一个命令后立即就能收到对方的响应,这时候就将PSH=1;

  • 复位RST:占1位,当RST=1,表明TCP连接中出现严重差错,必须释放连接,然后再重新建立连接;

  • 同步SYN:占1位,在连接建立时用来同步序号。当SYN=1,ACK=0,表明是连接请求报文,若同意连接,则响应报文中应该使SYN=1,ACK=1;

  • 终止FIN:占1位,用来释放连接。当FIN=1,表明此报文的发送方的数据已经发送完毕,并且要求释放;

  • 窗口:占2字节(16位),指的是通知接收方,发送本报文你需要有多大的空间来接受;

  • 检验和:占2字节(16位),校验首部和数据这两部分;

  • 紧急指针,占2字节,指出本报文段中的紧急数据的字节数;

  • 选项,长度可变,定义一些其他的可选的参数。

一系列数据下来,TCP的首部固定为20字节

运作方式

介绍完了TCP,我们来看一下TCP的三个阶段,
TCP协议的运行可划分为三个阶段:

  1. 连接创建(connection establishment)
  2. 数据传送(data transfer)
  3. 连接终止(connection termination)

这篇文章主要叙述的是 连接创建连接终止 两个阶段,也就是TCP的三次握手与四次挥手两个环节.

创建通路(三次握手:Three-way handshake)

  • TCP服务器进程先创建传输控制块TCB,时刻准备接受客户进程的连接请求,服务器就进入了 LISTEN 状态
  1. 第一次握手(SYN=1, seq=x):
    TCP客户进程也先创建传输控制块TCB,然后向TCP服务器发出连接请求报文。

    这时报文首部中的同部位 SYN=1,同时选择一个初始序列号 seq=x,此时,TCP客户端进程进入了 SYN-SENT(同步已发送状态) 状态。

    TCP规定,SYN报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号。

  2. 第二次握手(SYN=1, ACK=1, seq=y, ACKnum=x+1):
    TCP服务器收到请求报文后,如果同意连接,则发出确认报文。确认报文中应该 ACK=1SYN=1,确认号是ACKnum=x+1,同时也要为自己初始化一个序列号 seq=y

    此时,TCP服务器进程进入了SYN-RCVD(同步收到)状态。这个报文同样不能携带数据,而且要消耗一个序号。

  3. 第三次握手(ACK=1,ACKnum=y+1)
    TCP客户进程收到确认后,还要向TCP服务器给出确认。确认报文的ACK=1ACKnum=y+1,自己的序列号seq=x+1.

    此时,TCP连接建立,客户端进入ESTABLISHED(已建立连接)状态。

    TCP规定,ACK报文段可以携带数据,但是如果不携带数据则不消耗序号。

  • 当服务器收到客户端的确认后也进入ESTABLISHED状态,此后双方就可以开始通信了

TCP为何要三次握手,两次呢

  • 为什么TCP需要三次握手?如果只有两次呢?后果是什么?

    如果使用两次握手有可能会出现这种情况:
    客户端发送第一次的连接请求后,因为连接请求报文丢失而未收到确认,所以再次发送了一次请求,最后这次请求成功建立,并在数据传输完成后释放连接。
    如果第一次的连接请求只是在某个网络节点上滞留了一段时间且在第二次连接释放以后的某个时间才到达服务端,造成服务端误以为是客户端发起的新的请求,于是向客户端发出确认报文段,同意建立连接(未采用三次握手),新的连接又再次建立,此时客户端忽略服务端发来的确认,也不向服务端发送数据,造成服务端一直等待客户端发送数据,浪费大量服务端资源.

终结通路(四次挥手 Four-way handshake)

四次挥手也称作改进的三次握手,客户端或者服务器均可发起主动挥手动作

  • 第一次挥手(FIN=1,seq=x)
    假设客户端想要关闭连接,可以向服务端发送一个报头FIN=1的包,表明自己已经没有数据可以发送了,但是仍可以接收数.

    发送完毕后客户端进入 FIN_WAIT_1 状态,

  • 第二次挥手(ACK=1,ACKnum=x+1)
    服务器端确认客户端的 FIN 包,发送一个确认包,报头ACK=1ACKnum=x+1,表明自己接受到了客户端关闭连接的请求,但还没有准备好关闭连接.

    发送完毕后,服务器端进入 CLOSE_WAIT 状态,客户端接收到这个确认包之后,进入 FIN_WAIT_2 状态,等待服务器端关闭连接。

  • 第三次挥手(FIN=1,seq=y)
    服务器端准备好关闭连接时,向客户端发送结束连接请求,报头FIN=1 .

    发送完毕后,服务器端进入 LAST_ACK 状态,等待来自客户端的最后一个ACK.

  • 第四次挥手(ACK=1,ACKnum=y+1)
    客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入 TIME_WAIT状态,等待可能出现的要求重传的 ACK 包。

    服务器端接收到这个确认包之后,关闭连接,进入 CLOSED 状态。

    如果客户端等待了某个固定时间(两个最大段生命周期,2MSL,2 Maximum Segment Lifetime)之后,没有收到服务器端的 ACK ,认为服务器端已经正常关闭连接,于是自己也关闭连接,进入 CLOSED 状态。

2MSL

四次握手中也有问题,比如最后一次握手后,主动提出断开连接的一方需要再等候2MSL,为什么?

我们再看一遍简化的四次挥手:

  1. 首先主动发起断开连接请求的A向服务端发送FIN=1的报文
  2. 服务端收到A的报文,回复ACKnum
  3. 等待一段时间,B发出FIN=1的报文
    此时B还不能释放资源,因为它需要确认A收到了ACKnumFIN=1,所以还需要等待A发送的最后一个消息:ACKnum
  4. A发出最后一条消息,B成功收到
  • 当B收到A最后发来的消息时:
    在B的视线内B认为双方已经达成了同步,都可以释放资源并关闭连接了,此时B释放此次TCP连接占用的资源以及端口。所以B(服务端)无需产生wait time,直接释放.

  • 但在A看来,发出最后一条消息后,因为已经是四次挥手的最后一次,所以A没有办法确认B是否收到了这条消息,所以A会做出两种假设:

    1. B没有收到
      B会超时重新传递FIN,A接受到后,会重新发送ACKnum
    2. B收到了
      不会回复任何消息,需要一个相对安全的wait time确保B已经收到

出现的A等待2MSL就是取这两种情况等待时间的最大值,以应对最坏情况,来确保TCP连接的正常关闭
最坏情况就是:去向ACK消息最大存活时间(MSL) + 来向FIN消息的最大存活时间(MSL) = 2MSL

  • 等待2MSL后,A就可以安心释放TCP占用的资源、端口。如果不等:

    释放的端口可能会重连刚断开的服务器端口,这样依然存活在网络里的老的TCP报文可能与新TCP连接报文冲突,造成数据冲突,为避免此种情况,需要耐心等待网络老的TCP连接的活跃报文全部失效。

SYN攻击

  • 什么是SYN攻击
    在三次握手中,服务器发送 SYN-ACK 之后,收到客户端的 ACK 之前的 TCP 连接称为``半连接(half-open connect)。此时服务器处于 SYN_RCVD 状态。当收到 ACK 后,服务器才能转入 ESTABLISHED 状态.

    SYN 攻击指攻击客户端在短时间内伪造大量不存在的IP地址,向服务器不断地发送SYN包,服务器回复确认包,并等待客户的确认。
    由于源地址是不存在的,服务器需要不断的重发直至超时,这些伪造的SYN包将长时间占用未连接队列,正常的SYN请求被丢弃,导致目标系统运行缓慢,严重者会引起网络堵塞甚至系统瘫痪。

    SYN 攻击是一种典型的 DoS/DDoS 攻击。

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

  • 如何防御 SYN 攻击
    SYN攻击不能完全被阻止,除非将TCP协议重新设计。但可以减轻SYN攻击的危害,常见的防御 SYN 攻击的方法有如下几种:

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