关于TCP的那些事,都在这里了

最近又重新复习了一下TCP/IP协议,于是打算写个文章总结一下,避免以后又忘记。。。

直接上内容

1. TCP概述

如果要用一句话来描述TCP的话,那必须是:TCP是传输层的关键协议,它是可靠的、面向连接的、基于字节流的全双工协议。

首先,它可靠,确保数据按顺序到达,并在丢失时重新发送;其次,它是面向连接的,在发送数据之前必须通过三次握手来建立通讯;另外,TCP是基于字节流的,这意味着它可以灵活地传输任何大小的数据。而且不仅发送端可以启动连接,接收端也能做同样的事,是一个全双工的协议。

2. 首部字段

tcp首部字段包含了多种信息,这些信息用于指导TCP数据段的发送和接收,确保数据传输的可靠性。

  • 源端口号、目标端口号
  • 序列号(SYN):解决数据包的乱序、重复问题,根据序列号组装出正确的数据再传递给上层应用。确保数据的可靠、有效的传输,并使得TCP可以处理掉包、重传和乱序数据包的问题。
  • 确认号(ACK):确认传递的数据包已经到达,表示比ACK小的数据序列号已经全部接收完毕。
  • 数据偏移(Data Offset)
  • 保留(Reversed):为未来的添加或修改保留位置
  • 控制位(Flags):常用的有RST、SYN、PSH、FIN
  • 窗口大小(Window Size):表示当前能够接收的数据量
  • 校验和(Checksum):用于检查TCP首部和数据的正确性和完整性,当收到校验有差错的报文,不会进行确认,而是直接丢弃它
  • 紧急指针(Urgent Pointer)
  • 选项和填充(Options and Padding):可选的,用于控制参数,如MSS、SAC、时间戳等。

3. 分层

一般来说,分层有两种分层,一种为OSI七层模型(物理层、链路层、网络层、传输层、会话层、表示层、应用层),另一种为TCP/IP四层模型(应用层、传输层、网络层、链路层),这里我们主要说TCP/IP分层

TCP/IP分层

  • 应用层

    应用层包含了所有与网络应用相关的协议,如HTTP、FTP、SMDP、DNS等;主要为应用提供通信服务。

  • 传输层

    传输层为两台计算机上的应用提供了端到端的逻辑通信服务,确保数据可以从源主机传输到目的主机,传输层主要关注如何进行通信。主要协议有TCP、UDP协议。

  • 网络层

    网络层为两台主机之间负责数据包的路由和转发,确定数据如何从发送者传输到接收者,将传输层产生的数据段封装成分组数据包发送到目的主机,并提供路由选择的能力。主要协议有IP、IGMP(组播协议)。

  • 网络接口层

    网络接口层提供主机连接到物理网络所需要的硬件和相关的协议,主要为负责帧的传播、物理寻址等。蓝牙、以太网、Wi-Fi都属于这一层的。

小结

分层的好处: 本质是让复杂的问题简单化。每层关注特定功能,让网络设计、实施和问题解决更加清晰。它支持不同技术协同工作,简化了网络设备的互联互通。故障时,问题可以局限于单一层次,便于快速定位和处理。这种分层还让网络技术能够灵活发展,适应不断变化的需求。

这里多说两句,一开始接触分层结构的同学看见传输层和网络层的作用可能会有点疑惑,两者之间不都是负责两端的通信吗?这里打个比方说一下:网络层就像邮政系统,负责将你的信件送到正确的城市,这个过程可能碾转多地。而传输层则确保信件投递到特定的门牌号,还会告诉你信是否安全送达,如果丢了,它会帮你重新寄一封。

4. MTU和MSS

  • MTU工作在链路层,限定最大的数据帧的传输单元。由于以太网传输的大小是有限制的,使用最大的MTU不能超过数据传输链路的路径MTU,也就是说,最大的MTU不能超过最窄的路径。

  • MSS工作在传输层,限制了传输层给网络层数据的最大段的大小。由于MTU的限制,发送方会把传输层给的数据段进行切片,而MSS的作用就是将数据主动进行切片然后递交给网络层。 MSS = MTU - IP首部 - TCP首部

5. 三次握手和四次挥手(敲黑板!这是重点!)

  • 三次握手

    1. 客户端发送一个初始的序列号SYN给服务器,此时客户端进入SYN_SENT状态

    2. 服务端收到客户端的SYN后,回复一个ACK确认号和自己的初始序列号SYN,此时服务端进入SYN_RCVD状态

    3. 客户端收到服务端的ACK和SYN后,会对这个SYN进行确认,然后发送给服务器。此时客户端进入ESTABLISHED状态,服务端收到ack后随即进入ESTABLISHED状态,握手完成。

    自连接

    在一台主机里,一个进程监听一个端口进行连接请求,然后请求的主机刚好又是自己,就自己连接上了自己。

  • 四次挥手

    1. 客户端发送一个FIN序列号给服务器表示主动请求断开连接,此时客户端的状态为FIN_WAIT-1

    2. 服务器收到客户端的FIN,先对这个FIN进行确认,此时服务器进入CLOSE_WAIT状态

    3. 然后稍等一个定时器后服务器再回复一个FIN给客户端,此时服务器进入LACK_WAIT状态

    4. 客户端收到服务器的ACK后进入FIN_WAIT-2状态,接着收到FIN后,对这个FIN进行确认就进入WAIT_TIME状态,最后等待2个MSL计时器后进入CLOSED状态,服务器收到确认号进入CLOSED,连接关闭。

    同时关闭

    在挥手的时候,由于TCP是全双工的,两方同时发送一个FIN给彼此表示请求关闭,进入了FIN_WAIT-1状态,那么在对端收到FIN后,都进入了CLOSING状态,此时都会发送一个ACK给对端,进入WAIT_TIME状态,等待两个MSL就进入了CLOSED状态。这个过程中,双方同时请求关闭,都没有数据发送,可以跳过FIN_WAIT-2状态,3次挥手就可以成功。

    SYN和FIN都需要消耗一个序列号,因为只要需要ack确认的都需要消耗序列号。

    为什么需要四次挥手,三次可以吗?

    理论上是可以的,但是这个要确保服务器没有数据再传给客户端的前提下,因为客户端发送FIN时,表示客户端没有请求再发送,然后进入半关闭状态,但是服务端收到客户端的FIN后,是可以发送数据给客户端的,但是如果是3次挥手,同时回复ACK+FIN,那么就无法再发送数据就进入半关闭状态了,所以要先发送一个ack给客户端确认,以免客户端收不到进行重传,然后等一个计时器时间延长确认,如果没有数据发送了,就发送FIN进入半关闭状态。

    可以4次握手吗?

    理论上也是可以的,即把ack和syn分开来传输,但是这完全没有必要,因为在连接之初,并没有数据传输,而是进行同步双方的序列号,所以没必要分开传输消耗性能。

6. TCP首部时间戳

时间戳不是首部的一个固定字段,而是在option上的一个可选字段,由双方共同开启才会生效。主要为了计算RTT和解决PAWS问题,其中发送方在options中带Tval标识,而接收方收到后取发送方的Tval为自己的Tscr值,然后自己再递增生成一个Tval值发送给发送方。PAWS指解决序列号回绕问题,在高速的网络传输中,大量的数据传输可能会造成序列号重复的问题,导致不知道当前接受的数据是新的还是旧的,而有了时间戳就很好的解决了这个问题。

7. 半连接和全连接队列

半连接队列(SYN队列)和全连接队列(Accept队列)是服务器的概念,用于帮助服务器处理传入的TCP连接请求。半连接队列,指在服务器接收到SYN并且回复ACK和SYN时后,也就是第二次握手完成后,服务器会将连接放到半连接的队列中;全连接队列,指在服务器完成3次握手之后,连接被放到全连接队列中,然后等待应用accept取走使用。当这两个队列满了的时候,服务器会对新的请求丢弃或者延迟处理。

8. SYN Flood攻击

当半连接队列被填满时,服务器会丢弃新的连接请求。那么问题就来了,如果攻击者模拟大量的发送方,向接收方发送大量的SYN,表示要建立请求,而接收方并不知道这个SYN的真实性,就会回复ACK和SYN把请求转移到半连接队列,很快队列就会填满,而真正的用户想访问接收方尝试建立连接,但此时队列已经满了,使用没办法成功建立请求,这就是SYN flood请求。

解决的方法有扩大半连接队列、限制SYN的接收率、使用防DDos服务、SYN Cookie等。而SYN Cookie是最有效的方法,当半连接队列满时,使用特殊的算法计算出一个Cookie值,放在回复的ACK+SYN中,当发送方收到后在回复的ACK中带上这个Cookie,接收方收到后会校验这个Cookie的合法性,然后再给这个连接分配资源,本质上就是只有通过SYN Cookie建立的连接成功后才会分配资源,减少了服务器资源的使用。当启用 SYN cookies 时,服务器只有在三次握手成功完成后才为连接分配资源。这与传统的 TCP 握手行为不同,其中服务器在收到 SYN 请求并发送 SYN+ACK 响应后,即使连接尚未完全建立,也会为其分配资源。

9. 快速打开

快速打开指在已经建立了一次TCP握手的情况下,在下一次连接建立的过程中,可以在发送SYN的同时携带数据,这样可以减少一个RTT往返时间,加快数据传输的效率。正常情况下,两个端每次发送数据之前都需要先建立连接,而不能直接在SYN携带数据,而有了快速打开之后,在发送方法首次和接收方建立连接时,options有一个TCP Fast Open字段标识当前连接为了快速打开,而接收方接收到SYN后,生成一个Cookie,连同ack和syn一起返回给发送方,发送方接收后返回一个ack然后把cookie存在本地。当下一次需要和接收方连接时,在options中带上这个cookie,再携带数据一起发送,接收方接收后验证cookie的真实性,确认无误后即可以返回ack+syn+发送方需要的数据给发送方,如果cookie不正确则走正常连接流程。这样就可以减少一个RTT往返时间,更快一步的拿到数据。

10. 超时重传、快速重传、SACK(敲黑板!这是重点!)

超时重传指在发送端发送了数据之后,在等待接收端返回的过程中会启用一个计时器,当达到规定的时间后,还没有收到ack,就会再重新发送数据,确保了TCP的可靠性,重传间隔的时间遵循指数级退避。

快速重传指在数据传输的过程中,发送端发送一连串的数据给接收端,那么接收端就会对这些分段数据进行确认,在发送端收到3次或以上接收端传来的ack中,并没有包含前面已经发送的数据时,发送端就不会等超时再重新传数据,而是直接把缺失的数据传给接收端。

在快速重传的过程中,仔细观察就会发现如发送0 - 1000, 1001 - 2000,2001 - 3000的过程中,实际上接收端可能已经接收了1、3段数据了,但是ack只对第一段数据进行确定,而第三段并没有,那么发送端就不知道已经发送端2、3段数据需不需要重新发送。而有了SACK就可以解决这个问题,在返回的ack中带上sack,表示之前发送的数据哪些已经接收到了,重传sack没有包含的数据就可以了。

11. 超时重传的时间计算(RTO)

在重传的过程中,需要遵循一定的时间间隔进行重传,如果间隔太久接收端会等太久,如果间隔时间太短,已经传输的数据可能并没有丢失而是因为宽度等问题迟迟没有到,这个时候又重新传,就会造成或加大网络的堵塞,那么就需要使用到RTO。计算的方法有经典算法取RTT的平均时间,平滑的计算出重传的时间,主要是适用RTT波动较小的时候使用;标准算法,主要是适用RTT波动较大的时候使用。

12. 滑动窗口(敲黑板!这是重点!)

滑动窗口是TCP的一个重要的概念,主要用于控制发送方发送的数据流量,有发送窗口和接收窗口。

发送窗口 是在发送方维护的,用于控制发送给接收方的数据量。发送窗口的大小可以根据接收方的接收窗口动态调整。当发送方发送的数据量达到接收窗口的上限时,发送端就会达到 窗口已满(Window Full) 的状态,此时发送方在途的数据已经等于接收窗口大小。除非接收到ACK确认,否则发送方不会再发送更多数据。

接收窗口 则是在接收方维护的,指示接收方目前能够接收的数据量。在高速的网络传输中,如果接收方接收数据的速度很快,但处理和移出缓存的速度不够迅速,会导致接收端的缓存填满。在这种情况下,接收端会通过发送窗口更新(Window Update)通知发送方其窗口大小,可能会通告一个较小的窗口大小,甚至是零窗口(Zero Window) ,后者表示接收窗口已满,暂时无法接收新数据,发送窗口应当调整为0。

为了应对零窗口情况,TCP实现了 零窗口探测(Zero Window Probe) 机制。当发送方的窗口大小为0时,它会周期性地发送探测消息到接收方,以便确定接收方是否已准备好接收更多数据。当接收方的窗口不再是零时,它会响应探测,发送方随后可以恢复数据发送。

13. 拥塞控制(敲黑板!这是重点!)

滑动窗口整体上是控制发送端和接收端的数据流量,控制的是自身,而拥塞控制则不同,把持着整个网络的传输数据流量,主要算法有慢启动、拥塞避免、快速重传、快速恢复。

为了实现这些算法,每一个TCP连接都包含了拥塞窗口慢启动阀值的概念。与接收窗口不同,拥塞窗口是控制自身在收到对端的ack确认之前,还能传输的最大MSS段数,发送窗口的大小等于拥塞窗口和接收窗口两者的最小值,也就是说发送窗口受接收端和网络传输快慢的限制,不能比两者都大,拥塞控制算法本质上是控制拥塞窗口大小的变化。

慢启动是指在TCP连接后,两端各自初始化自己的拥塞窗口大小,由一个固定的值开始,每确认一个ack就会时cwnd+1,所以每往返一个RTT就会指数级的递增,以达到最适合网络传输的大小。

由于慢启动算法的指数级增长,拥塞窗口很快就会变得很大,如果不加以限制很容易造成网络的堵塞,于是有了拥塞避免算法,该算法规定,当cwnd增长到一定的阀值大小时,变为线性级增长放缓增长速度,每经过一个RTT,cwnd+1个MSS段大小,以避免网络的拥塞。

快速恢复算法是发生在快速重传之后,当发送端开始重传丢失的数据时,并不会遵循慢启动算法的方式,而是将cwnd的阀值减半,直接将cwnd的大小设置为阀值的大小,然后遵循线性增长的进行传输,加快网络的吞吐量。

14. Nagle算法

Nagle的设计是为了减少小数据包在网络中的传输,减少带宽的浪费。在网络传输中,每发出一个包都会包含TCP和IP头部信息,这些信息大小一般都为40字节,而如果在传输的过程中,太小的一个包都进行一个tcp传输,那么就会非常浪费带宽,而Nagle算法就是为了避免传输大量的小包而出现的。

Nagle的工作原理

  • 在首次传输数据中,不论大小,都会立即发送出去。
  • 而在此之后,只有满足以下两个条件之一才会发送更多数据
    1. 接收端已经对前面发送的所有数据都进行确认
    2. 要发送的数据达到MSS段大小
  • 在等待ACK确认的过程中,如果积累了很多小包,那么Nagle算法会将这些包合并到一个MSS段大小,然后再发送

15. 延迟确认

在传输过程中,接收端每收到一个数据包都需要对其进行ACK确认,而如果每次进行确认都只回复一个ACK,那么就会造成很大的浪费。延迟确认允许接收端收到数据后,延迟一定的时间再进行确认,而不是马上就进行确认。这样就可以减少网络上的ACK确认包的数量,降低网络和主机的负载。

延迟确认工作原理

  • 接收端收到一个TCP包,并不会立即的进行确认,而是开启一个定时器
  • 在定时器到期之前,如果接收端收到了很多包,不会每个包都回复一个ACK确认,而是累积成起来,等定时器到期时,发送一个累积确认ACK,确认这个ACK之前的所有包都已经收到了。
  • 同样,在定时器到期之前接收端如果有数据要发送给发送端,那么ACK就可以搭上顺风车一起返回给发送端。

延迟确认是默认开启的功能,可以提高网络的效率,但是有些情况下也会给传输的过程造成一定的负担。如Nagle算法和延迟确认一起工作的话,就会出现严重的性能问题。试想一下,Nagle算法是把多个包攒成一个一起发送,而延迟确认是收到包不马上确认,两边互等浪费时间。

16. keepalive

Keepalive是检测网络连接是否仍然存在的机制,当一个TCP连接已经建立后,长期没有数据传输,Keepalive机制就会发送一个探测信息,接收端必须响应这个信息,否者发送端会假设这个连接已经断开而关闭这个连接。

Keepalive工作原理

  • 在启用了keepalive的TCP连接成功后,如果在一定的时间内没有数据在连接上交换,发送端就会发送一个Keepalive探测包,这个探测包是为了探测对方是否还存在,不包含任何数据。

  • 如果接收端收到这个探测包会进行确认一个ACK,表示连接是活动的,并且继续保持连接状态

  • 如果发送端长时间没有收到这个ACK确认,那么就会重复发送探测包,在重复了一定的次数后还没有收到确认包,就默认连接已经断开,然后关闭这个连接。

17. 队头阻塞

队头阻塞指的是在一系列的消息队列中,第一个消息因为某种原因延迟处理,而导致了后面的消息即使已经到达了但是还不能被及时处理。

在TCP中,队头的阻塞是因为TCP的严格顺序而引起的,由于TCP保证包的顺序到底,因此即使后面的数据先到达,但是仍然不能够被处理。例如,有A、B、C 3个包,B和C先到达了,而A由于某种原因延迟或丢失,迟迟没有到达,接收端必须等待A的到达才会对这一组消息进行处理,结果B和C就被A给阻塞了。

完结!

以上就是关于TCP的主要内容总结了,经过这一次码下来,记忆加深了不少!如果觉得内容对你有帮助,麻烦可以动一下发财的小手点个赞!如果觉得我哪里总结的不对不准确也请在评论区给指出!最后感谢大佬写的小册,对我帮助不少!深入理解 TCP 协议:从原理到实战

相关推荐
hzyyyyyyyu6 小时前
隧道技术-tcp封装icmp出网
网络·网络协议·tcp/ip
用户3157476081357 小时前
成为程序员的必经之路” Git “,你学会了吗?
面试·github·全栈
布川ku子8 小时前
[2024最新] java八股文实用版(附带原理)---Mysql篇
java·mysql·面试
有趣的杰克15 小时前
移动端【01】面试系统的MVVM重构实践
面试·职场和发展·重构
进击的程序汪15 小时前
深入理解网络监听:TCP、UDP、IPv4 和 IPv6
网络·tcp/ip·udp
勤奋的凯尔森同学19 小时前
ubuntu22.04上手指南(更新阿里源、安装ssh、安装chrome、设置固定IP、安装搜狗输入法)
chrome·tcp/ip·ssh·ubuntu22.04·搜狗输入法·ubuntu24.04
hgdlip19 小时前
家里电脑ip地址怎么设置?详细指导
网络·tcp/ip·智能路由器·家里电脑
米饭是菜qy21 小时前
TCP 三次握手意义及为什么是三次握手
服务器·网络·tcp/ip
yaoxin52112321 小时前
第十九章 TCP 客户端 服务器通信 - 数据包模式
服务器·网络·tcp/ip
saturday-yh1 天前
性能优化、安全
前端·面试·性能优化