TCP协议十八连问

526 阅读10分钟

引言

在上一篇文章:《HTTP十六连问》 中,曾提到“HTTP2最大的缺陷在于它依然没有在实质性地解决队头阻塞的问题,只是把它交给了TCP层,也就是计算机网络所说的上层依赖下层的服务”。如果接下来被询问到TCP相关的问题,该如何回答呢?

那TCP是如何解决拥塞问题的?

TCP有拥塞控制,用到的算法可以用6个字概括:“慢启动、快重传”。

流量发送不仅由收发双方决定,还由真实的网络环境决定。

慢启动-快重传的算法如图所示:(图来源于《computer network》)

image.png

  • 横坐标是时间的推移,单位是RTT(round trip time),纵坐标是当前发送的窗口大小
  • 所谓慢开始,其实也不是真的慢,因为它是指数级增长的 ,只是从1开始,而不从发送方要求的值开始。
  • 初始会设定一个阈值(threshold),窗口大小达到阈值后,转为线性增长(慢下来)
  • 当窗口大小超过接收方所能承受的最大长度后,会发生丢包,于是这时发送窗口大小又会快速重新减少为初始值,这就是重传。
  • 重传开始后,新的阈值会被设定为上次导致发生超时的最大窗口的一半。
  • 这个过程会不断重复,保证流量窗口是不断伸缩,但又在可控范围内。

TCP还有什么特点呢?

面向连接的、可靠的、基于字节流传输

那可靠性除了体现在拥塞控制,你还能想到别的内容吗?

比如流量控制和差错控制、(序号+确认)——>保证有序性、定时器——>超时重传,保证完整性

但其实这些概念是贯穿多个层的,并不是某一层独有,往往是上层调用下层的服务,而且算法思想也是类似的。早在接触链路层时我就已经接触过流量控制和差错控制的概念了。

以下先说说在链路层中被介绍过的背景知识:

先说说差错控制:

  • 对于发现错误和纠正错误,有检错码(奇偶校验、CRC校验等)和纠错码(汉明码能检错也能纠错)
  • 对于发生错误后的重传,有滑动窗口协议,它分为三类:
    • 停-等协议
    • 回退n帧(发送方记录状态,重发出现错误那一帧往后的所有内容)
    • 选择性重传(接收方记录状态,要求发送方发送出现错误的那一帧即可)

接下来说说流量控制: 其实流量控制和拥塞控制有一点像,但又有不同:

  • 拥塞控制是一个全局性的问题,必须使整个网络的主机、路由器、线路等资源或能力相匹配,目的是保障子网能够正常地传输分组。
  • 流量控制是一个一对一的问题,使发送方和接收方的能力匹配,通过一定的机制保证发送方发送的速度不至于淹没接收方。

流量控制的思路大致分为两类:

  • 基于反馈的流控制
  • 基于速率的流控制

前者是链路层中介绍的,用滑动窗口的机制去实现,但发送窗口的移动依赖于确认帧的到达;而后者是传输层中特有的特性,也就是TCP会采用的流控制方法。

具体流程是这样的:

  • TCP的窗口大小是其缓冲区的尺寸,建立连接的双方都将分配一个缓冲区作为接收数据的 存放空间,并相互通知对方,此后,每次对接收数据确认的同时发布一个窗口公告(window advertisement),报告剩余窗口的大小,任何时刻,剩余的缓冲区空间的数量窗口大小
  • 发送方收到一个零窗口公告时,必须停止发送,直到接收方重新发送一个非零的窗 口公告,但有两种情况可以除外:
    • 发送紧急数据,如允许用户终止(kill)当前正在远端机上运行的进程
    • 发送方可以发送一个字节的数据段通知对方,要求接收方重申希望接收的下一字节及当前的窗口大小,以防止可能的窗口公告丢失而导致的死锁

下图完整地展现了这一过程:(图来源于《computer network》)

image.png

TCP和UDP的区别是什么?

TCP和UDP的协议字段不同,因为它们的目标不同。UDP字段只有四个部分。

  • TCP可提供有连接的服务,有上述提到的拥塞控制、流量控制、差错控制等等,需要保证数据有序到达;UDP是无连接的,它不保证可靠的传输,不保证数据按顺序到达,甚至不保证它能到达。
  • 另外,由于TCP需要建立连接,所以它是一对一的,而UDP可以是一对一,一对多,多对多的交互通信。

还有一些比较细微的点:

  • TCP是流式传输的,UDP是逐包逐包发送。

(不知道这点和下图有没有联系,图源【计网复习随手记】(一)

image.png

  • TCP 的数据大小如果大于 MSS(message segment size) 大小,则会在传输层进行分片;UDP 的数据大小如果大于 MTU(message transmission unit) 大小,则会在 网络层进行分片

TCP和UDP各自的应用场景?

  • 有些服务是必须要保证可靠数据传输的,比如文件传输(使用FTP等)、远程登录(SSH)、HTTP/HTTPS等
  • 像音视频这类,并不要求数据完整,偶尔一两帧发生错误或丢失不影响整体体验,但对实时性要求高(允许出错,但不允许延时),就基于UDP
  • 像DNS用的就是UDP,支持HTTP3的QUIC也是基于UDP的
  • 前面提到UDP可以一对多、多对多,因此可用于广播通信

TCP和UDP可以使用同一个端口吗?

结论就是:经过测试,是可以的。

但为什么是这样,要涉及到对一些抽象概念的理解。

要解答这个问题,首先得明确端口是什么。

直观地理解,在启动项目服务的时候,比如"http://localhost:8080" ,冒号后面的8080就是端口号。端口实际上是为了区分主机上的不同进程,比如A进程占用了a端口,B进程就不能再占用b端口了。端口号的划分是相对主机而言的,不同主机的端口号是相互独立的。

由上述表述可以明确一件事情:同一台主机的同一个端口不能被两个进程占用(但也有特例,父子进程)。

而TCP和UDP是进程吗?不是,它们是两种不同的协议,对应于两个不同的软件实体。

我的理解是在计算机网络中提到的进程又和操作系统的不太一样,这里的进程指的是应用层级别的运行中的程序,所以相对应地,TCP和UDP是在它之下的概念。

而在ip的协议字段中,有一个protocol字段,当为6时表示上层为TCP,17时为UDP。 (图片来源【计算机网络】网络层(二)ipv4头部分析

image.png

通过这个字段可以确定数据之后是发送给TCP/UDP这两个模块中的哪一个进行处理。但从这里开始,两者就已经分开了。虽然TCP的端口和UDP的端口都是端口,但它们是两个概念,是相互独立的,类似于有两个人都是叫小明,但他们并不是同一个人。

因此不会产生冲突。

关于UDP

UDP有差错控制吗?

UDP协议中有checksum字段,它能检错,但不能纠错。

UDP一定是不可靠的吗?

不,由它构建的应用可通过自身建立可靠性机制,比如Chrome用QUIC

那关于QUIC你能再详细说说吗?

它由Google在2012年研发及部署,并已在2021年被写入RFC9000标准。 QUIC有几大基本特性:

  • 快速建立连接
  • 基于速率的拥塞控制算法
  • 差错控制
  • 重传机制

因为有着和TCP很相似的特性,它被戏称为"TCP/2"

(它还保证了通信的前向安全:指用来产生会话密钥的长期密钥泄露出去,不会泄漏以前的通讯内容,具体措施是每次会话都创建一个新的通信密钥)

关于TCP的连接细节

TCP三次握手的流程?

面试时可以回忆这张图,重点描述这几个字段的状态变化。 img_v2_b3a7a753-7c4b-496a-bdde-51d40b3b5d9g.jpg

  • 为什么ack=seq+1?因为ACK可理解为“我同意接收,你上次是到seq,请你记住你下一次开始发送的位置,从seq+1开始”
  • 其中的ACK和SYN指的是协议字段中的相应位置1;seq和ack则是32bits对应的域。

img_v2_3a391ac6-a33c-4470-99f8-d31ba8da325g.jpg

为什么是三次握手?

笔者初学计算机网络时,看完课本后曾写下了这篇文章【计算机网络】再谈传输层(一)杂谈:TCP为什么是三握四挥?不这样做会如何

里面列举了几种可能的连接失败的情况(虽然现在回头看发现不全)。

三次握手应该是经过多次验证能得出的最优解,它可以保证如下几点:

  • 阻止重复历史连接的初始化

比如若网络拥堵导致初始连接请求没被接收,第二次又发送,后面新旧两次连接都到达了服务端,就可能导致服务端重复响应和连接

  • 同步双方的初始序列号

见上述说的ACK的含义

  • 第三次握手恰到好处地避免资源浪费

如果没有客户端的第三次握手,服务端得去发送请求询问是否已建立连接,如果这一过程因为网络阻塞重复多次,就会造成资源浪费

没有accept可以建立连接吗?

从上图可知,accept没有直接参与三次握手的过程,所以是可以的。

没有listen可以建立连接吗?

那要看是什么连接,有一种TCP自连接机制,指的是客户端自己和自己建立了连接,这种情况下不需要服务端参与,也不需要listen。

为什么需要TIME_WAIT?

参考两军问题,没有回应也是一种回应。能确保被动关闭的一方最终也能正确被关闭。

TIME_WAIT过多有影响吗?如何避免?

影响:占用端口资源(客户端)、系统资源(服务端)(CPU、内存等等)

措施:其实time_wait是一种好的设计理念,有一些强制措施,但治标不治本,最好的原则就是服务器不要主动断开连接,让客户端去断。

  • 有个reuse和timestamp选项
  • 有个buckets
  • 通过SO_LINGER,发送RST来强制关闭连接,跳过TIME_WAIT

那什么时候服务器会主动断开连接呢?

  • HTTP请求达到上限
  • HTTP没有使用keep-alive或者keep-alive超时

建立连接后服务端宕机会发生什么?

服务端会发送FIN报文,接下来进行四次挥手操作。

为什么四次挥手还能正常执行? 因为TCP连接信息是由内核维护的,挥手过程也是通过内核完成,出现故障后TCP连接资源将会被内核回收,并完成挥手操作。

如何应对连接后客户端宕机这种情况?

参考上面提到的keep-alive机制,超时服务器会自动断连(比如ssh上去服务器如果没有设置seconds between keep-alive,长时间没操作后就会断开连接)

参考文献

  • 《computer network the fifth edition》
  • 《计算机网络自顶向下方法》
  • 小林coding----TCP三次握手和四次挥手的面试题(2023最新版)