1. 简介

TCP是一个面向连接的、可靠的、基于字节流的传输层协议。

  • 面向连接:一定是「一对一」才能连接,不能像 UDP 协议可以一个主机同时向多个主机发送消息,也就是一对多是无法做到的;
  • 可靠的:无论的网络链路中出现了怎样的链路变化,TCP 都可以保证一个报文一定能够到达接收端;
  • 字节流:消息是「没有边界」的,所以无论我们消息有多大都可以进行传输。并且消息是「有序的」,当「前一个」消息没有收到的时候,即使它先收到了后面的字节,那么也不能扔给应用层去处理,同时对「重复」的报文会自动丢弃。

2. 报文头格式

  • 源端口:数据从哪来
  • 目的端口:要到哪里去
  • 序列号:在建立连接时由计算机生成的随机数作为其初始值,通过 SYN 包传给接收端主机,每发送一次数据,就「累加」一次该「数据字节数」的大小。用来解决网络包乱序问题。
  • 确认应答号:指下一次「期望」收到的数据的序列号,发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。用来解决丢包的问题。
  • 数据偏移:表示 TCP 报文段的首部长度,4 位二进制最大表示15,由于TCP首部包含个可变长度选项,需要指定这个 TCP 报文段到底有多长。它指出 TCP 报文段的数据起始处距离 TCP 报文段的起始处有多远。该字段的单位是4字节,所以TCP首部最大154 = *60字节
  • 控制位:
    • URG:表示本报文段中发送的数据是否包含紧急数据。为 1 时表示紧急指针有效,为 0 则忽略紧急指针。
    • ACK:表示确认字段是否有效。为 1 表示有效,0 表示无效,带 ACK 标志的TCP报文段称为确认报文段
    • PSH:提示接收端需立即从 TCP 接收缓冲区中读走数据,为接收后续数据腾出空间。为 1 表示对方应当立即把数据提交给上层应用,如果应用程序不将接收到的数据读走,就会一直停留在 TCP 接收缓冲区中。
    • RST:重置连接标志,用于重置由于主机崩溃或其他原因而出现错误的连接。或者用于拒绝非法的报文段和拒绝连接请求,带 RST 标志的 TCP 报文段称为复位报文段
    • SYN:建立连接时用来同步序号。SYN=1 说明这是一个请求建立连接或同意建立连接的报文。只有在前两次握手中 SYN 才置为1,带 SYN 标志的 TCP 报文段称为同步报文段
    • FIN:通知对方本端要关闭连接了,标记数据是否发送完毕。如果 FIN=1 告诉对方释放连接,带FIN标志的TCP报文段称为结束报文段
  • 窗口:用来告知发送端:接受端的缓存大小,以此控制发送端发送数据的速度,从而达到流量控制。16bit。
  • 校验和:奇偶校验,此校验和是对整个的 TCP 报文段,包括 TCP 头部和 TCP 数据,以 16 位字进行计算所得。由发送端计算和存储,并由接收端进行验证。提供额外的可靠性。
  • 紧急指针:标记数据为紧急数据。

3. TCP 和 UDP

TCP 和 UDP 区别:

  1. 连接

    • TCP 是面向连接的传输层协议,传输数据前先要建立连接。
    • UDP 是不需要连接,即刻传输数据。
  2. 服务对象

    • TCP 是一对一的两点服务,即一条连接只有两个端点。
    • UDP 支持一对一、一对多、多对多的交互通信
  3. 可靠性

    • TCP 是可靠交付数据的,数据可以无差错、不丢失、不重复、按需到达。
    • UDP 是尽最大努力交付,不保证可靠交付数据。
  4. 拥塞控制、流量控制
    • TCP 有拥塞控制和流量控制机制,保证数据传输的安全性。
    • UDP 则没有,即使网络非常拥堵了,也不会影响 UDP 的发送速率。
  5. 首部开销
    • TCP 首部长度较长,会有一定的开销,首部在没有使用「选项」字段时是 20 个字节,如果使用了「选项」字段则会变长的。
    • UDP 首部只有 8 个字节,并且是固定不变的,开销较小。
  6. 传输方式
    • TCP 是流式传输,没有边界,但保证顺序和可靠。
    • UDP 是一个包一个包的发送,是有边界的,但可能会丢包和乱序。
  7. 分片不同
    • TCP 的数据大小如果大于 MSS 大小,则会在传输层进行分片,目标主机收到后,也同样在传输层组装 TCP 数据包,如果中途丢失了一个分片,只需要传输丢失的这个分片。
    • UDP 的数据大小如果大于 MTU 大小,则会在 IP 层进行分片,目标主机收到后,在 IP 层组装完数据,接着再传给传输层,但是如果中途丢了一个分片,则就需要重传所有的数据包,这样传输效率非常差,所以通常 UDP 的报文应该小于 MTU。
  8. 应用场景
    • TCP 常用于 FTP 文件传输、HTTP/HTTPS。
    • UDP 常用于视频、音频、广播通信。

4. 三次握手

  1. 一开始客户端跟服务器都处于 closed 状态,然后服务端主动监听某个客户端端口,此时服务端处于listen 状态。
  2. 客户端随机初始化序列号 seq = client_isn,同时将 SYN = 1 表示这是SYN 报文,接着把该 SYN 报文发给服务器,注意此时报文不包含引用层数据,客户端处于 syn-sent状态。
  3. 服务端收到客户端的 SYN报文后也随机初始化个序号 seq = server_isn,并且将确认序号 ACK = client_isn + 1,接着把 SYN = 1跟 ACK = 1,然后该报文发送给客户端,服务器处于 syn-rcvd状态。
  4. 客户端收到服务器的报文后,将 ACK = 1,确认应答号 ACK = server_isn + 1,然后把报文发送给服务器,本次报文可发送数据,同时客户端处于established 状态。
  5. 服务器收到客户端的应答报文后,也进入 established 状态。
  6. 客户端和服务端建立好了连接,可以相互发送数据。

这里你可能发现了客户端跟服务器的初始化序列号是各自随机的,原因是网络中的报文会重发、会延迟、也有可能丢失,为避免相互影响干脆各用各的为好。同时通过流程发现前两次握手是不带数据的,第三次可携带数据

4.1 为什么握手需要三次?

  1. 避免历史连接

    客户端建立连接时发送多次 SYN 报文,由于网络拥堵可能旧的 SYN 报文比新的 SYN 报文先到服务器,服务器不管新旧,收到就回复 SYN + ACK 给客户端,三次握手情况下客户端可以根据序列号或超时时间判断回复的连接是否是历史连接,如果是历史连接直接发送 RST 报文给服务端来终止连接。

  2. 同步双方序列号

    TCP 协议的通信双方都在维护各自的序列号,且必须要让对方知道。只有通过三次握手才可以实现。

  3. 避免服务端资源浪费

    二次握手情况下,如果客户端的 SYN 阻塞导致重复发送多次 SYN 报文,那么服务器在收到请求后就会建立多个冗余的无效连接,造成不必要的资源浪费。而三次握手发现无效连接可在第三次给服务器端发送终止指令。

5. 四次挥手

  1. 客户端停止发送数据并且发出释放连接的报文,报文中FIN = 1,FIN 报文段即使不携带数据,也要消耗一个序号,此时序列号 seq = u ,u = 前面已经传输过来数据最后一个字节序号加 1,客户端进入 FIN-WAIT-1 状态。
  2. 服务器收到连接释放报文,发出确认报文,ACK = 1,应答确认号 ACK = u + 1,并且带上自己的序列号 seq = v,此时服务端就进入了 CLOSE-WAIT 状态。TCP 服务器通知高层的应用进程进入半闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个 CLOSE-WAIT 状态持续的时间。
  3. 客户端收到服务器的确认请求后,此时客户端就进入 FIN-WAIT-2 状态,等待服务器发送连接释放报文,在这之前还需要接受服务器发送的最后的数据。
  4. 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN = 1,ACK = u + 1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为 seq = w,此时服务器就进入了 LAST-ACK 状态,等待客户端的确认。
  5. 客户端收到服务器的连接释放报文后必须发出确认,ACK = 1,ACK = w + 1,seq = u + 1,此时客户端就进入了TIME-WAIT 状态,服务端进入 CLOSED 状态。注意此时 TCP 连接还没有释放,经过 2MSL 时间后自动进入 CLOSED 状态。

5.1 为什么挥手需要四次?

其实分析下整个关闭的流程就知道为什么必须是四次挥手而不是三次挥手了。

  1. 关闭连接时,客户端向服务端发送 FIN 时,仅仅表示客户端不再发送数据出去了但是还是能接收数据。
  2. 服务器收到客户端的 FIN 报文时,先回一个 ACK 应答报文,意思是不再接受数据了,但服务端可能还有数据需往外发送,等服务端不再发送数据时才发送 FIN 报文给客户端来表示同意现在关闭连接。这里注意服务端的 ACK 跟 FIN 是分开发的。
  3. 客户端收到服务端的 ACK 后,再给服务端发送 ACK,最终客户端跟服务器都进入close 状态。

5.2 TIME-WAIT 为什么是 2MSL?

MSL: Maximum Segment Lifetime 报文最大生存时间,意思是网络传输的报文在网络上存在的最长时间,超过这个时间报文将被丢弃。

网络中可能存在来自发送方的数据包,当这些发送方的数据包被接收方处理后又会向对方发送响应,所以一来一回需要等待 2 倍的 MSL

TIME-WAIT 是从客户端接收到 FIN 后发送 ACK 开始计时的。如果在 TIME-WAIT 时间内,因为客户端的 ACK 没有传输到服务端,客户端又接收到了服务端重发的 FIN 报文,那么 2MSL 时间将重新计时。

5.3 为什么需要 TIME-WAIT 状态?

主动发起关闭连接的一方,才有 TIME-WAIT 状态。

需要 TIME-WAIT 状态,主要有两个原因:

  • 防止旧的数据包被接收:上一次连接时候如果有网络震荡导致服务端数据在网络游荡,如果因为 time_wait 时间太短,新的连接可能会重新接受到游荡的消息。有了延迟时间可以避免消耗游荡的数据。
  • 确保「被动关闭连接」的一方能正常关闭:TIME-WAIT 作用是等待足够的时间以确保最后的 ACK 能让被动关闭方接收,从而帮助其正常关闭。

TIME-WAIT 过大的危害:

  • 占用内存资源
  • 占用端口资源

避免 TIME_WAIT 过多

  • 取消短连接,改用长连接方式。

  • 设定阈值,一旦超过阈值系统会将所有 TIME-WAIT 连接重置。

  • 修改客户端程序代码。

短连接:通信双方有数据交互时,就建立一个 TCP 连接,数据发送完成后,则断开此 TCP 连接.

长连接:数据发送完成后发检测包以维持此连接,直到主动断开连接。

评论




博客内容遵循 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议

载入天数...载入时分秒... 本站使用 Volantis 作为主题 鲁ICP备-20012065号