TCP快问快答

TCP和UDP的特点, 不同之处?

  • TCP是面向连接的(传输数据需要建立连接), UDP是面向无连接的(传输数据不需要建立连接)
  • TCP是可靠传输, 传输的数据有序可靠不丢不重. UDP是不可靠传输, 只能是最大努力交付.
  • UDP实时性更好, 适合高速传输或者对实时性要求比较高的场景. TCP适合对可靠性要求较高的场景, 如文件传输.
  • TCP传输效率比UDP低, 因为TCP传输时需要连接确认重传等机制保证可靠传输. UDP传输效率更高

使用 TCP 的协议有哪些?使用 UDP 的协议有哪些?

  • 基于TCP: HTTP, HTTPS, FTP, SMTP, SSH(远程登录会话协议)
  • 基于UDP: DNS, DHCP(动态配置 IP 地址)

TCP 粘包是怎么产生的?怎么解决?

为什么会TCP会有粘包现象?

  • TCP 传输数据基于字节流,从应用层到 TCP 传输层的多个数据包是一连串的字节流是没有边界的,而且 TCP 首部并没有记录数据包的长度,所以 TCP 传输数 据的时候可能会发送粘包和拆包的问题
  • UDP 是基于数据报传输数据的,UDP 首部也记录了数 据报的长度,可以轻易的区分出不同的数据包的边界

造成粘包和拆包现象的原因

  • TCP 发送缓冲区剩余空间不足以发送一个完整的数据包,将发生拆包
  • 要发送的数据超过了最大报文长度的限制,TCP 传输数据时进行拆包
  • 要发送的数据包小于 TCP 发送缓冲区剩余空间, TCP 将多个数据包写满发送缓冲区一次发送出去,将发生粘包
  • 接收端没有及时读取 TCP 发送缓冲区中的数据包,将会发生粘包

粘包拆包的解决方法

  • 发送端给数据包添加首部,首部中添加数据包的长度属性,这样接收端通过首部中的长度字段 就可以知道数据包的实际长度了
  • 针对发送的数据包小于缓冲区大小的情况,发送端可以将不同的数据包规定成同样的长度,不 足这个长度的补充 0,接收端从缓冲区读取固定的长度数据这样就可以区分不同的数据包
  • 发送端通过给不同的数据包添加间隔符合确定边界,接收端通过这个间隔符合就可以区分不同的数据包

TCP 如何保证可靠性

  • 校验和: TCP报文头有检验和字段, 可以用于校验报文是否损坏

  • 序列号与确认号: TCP 发送端发送数据包的时候会选择一个 seq 序列号,接收端收到数据包后会检测数据包的完整性与顺序性, 可以根据序列号进行排序去重,如果检测通过会响应一个 ack 确认号表示收到了数据包

  • 确认与超时重传机制: 接收方收到报文后就会返回一个确认报文, 发送方发送一段时间后没收到确认报文就会进行超时重传.

    • 快速重传: 接收方收到比期望序号大的报文段到达时, 就会发送一个冗余ACK, 指明下一个期待字节的序号,发送端接收到3个以上的重复ACK, TCP就意识到数据发生丢失,需要重传
  • 流量控制: 当接收方来不及处理发送方的数据,能通过滑动窗口,提示发送方降低发送的速率,防止包丢失

  • 拥塞控制: 当网络拥塞时,通过拥塞窗口,减少数据的发送,防止包丢失。

TCP流量控制

TCP 利用滑动窗口实现流量控制。流量控制是为了控制发送方发送速率,保证接收方来得及接收。 接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将窗口字段设置为 0,则发送方不能发送数据

TCP拥塞控制

在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏。这种情况就叫拥塞。拥塞控制就是为了防止过多的数据注入到网络中,这样就可以使网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提,就是网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及到所有的主机,所有的路由器,以及与降低网络传输性能有关的所有因素。相反,流量控制往往是点对点通信量的控制,是个端到端的问题。流量控制所要做到的就是抑制发送端发送数据的速率,以便使接收端来得及接收。

为了进行拥塞控制,TCP 发送方要维持一个拥塞窗口(cwnd)。拥塞窗口的大小取决于网络的拥塞程度,并且动态变化。发送方让自己的发送窗口取为拥塞窗口和接收方的接受窗口中较小的一个。

慢开始算法 -- Slow Start

所谓慢开始,也就是TCP连接刚建立,一点一点地提速,试探一下网络的承受能力,以免直接扰乱了网络通道的秩序。

  • 连接建好的开始先初始化拥塞窗口cwnd大小为1,表明可以传一个MSS(最大报文段长度)大小的数据。
  • 每当收到ACK确认报文(每经过一个往返时延RTT), cwnd大小直接乘以2, 呈指数让升, 也就是一次发送2^n个报文
  • 还有一个ssthresh, 是一个门限值(默认为16), 当cwnd >= ssthresh时,就会进入"拥塞避免算法"
拥塞避免算法 -- Congestion Avoidance

如同前边说的,当拥塞窗口大小cwnd大于等于慢启动阈值ssthresh后,就进入拥塞避免算法。

  • 让拥塞窗口 cwnd 缓慢增大,即每经过一个往返时间 RTT 就把发送放的 cwnd 加 1.
  • 过了慢启动阈值后,拥塞避免算法可以避免窗口增长过快导致窗口拥塞,而是缓慢的增加调整到网络的最佳值。
快重传与快恢复
  • cwnd大小缩小为当前的一半
  • ssthresh设置为缩小后的cwnd大小
  • 然后进入拥塞避免算法 (加法增大)

三次握手

为什么要三次握手,两次不行吗?为什么?

举个例子,客户端和服务器收发消息就像两个人对话。对话的双方必须要保证双方的嘴和耳朵都没有问题,也就是说两个人对话前,必须保证两个人不是哑巴,都能说话,也都不是聋子,都能听到声音。只要这样两个人才能顺序沟通。

  • 第一次握手:客户端向服务端发送报文,服务器收到报文。此时服务端知道了客户端说话功能没有问题,自己能收到报文说明自己的听话能力没有问题。
  • 第二次握手:服务端向客户端发送报文,客户端接收报文。此时客户端知道了自己之前发送的报文服务端收到了,并且服务端也发送了数据自己也收到了。此时客户端知道,自己的发送和接收能力没有问题,对方了发送和接收能力没有问题。所以在客户端的视角中,双方的收发能力都没有问题,可以开始正式发送数据了。所以在第三次握手的时候,客户端发送的报文可以携带数据给服务端。
    • 为什么需要第三次握手?因为客户端知道了自己收发能力没问题,此时服务器端并不知道。因为服务器端不确定自己报文有没有发出去,也不确定客户端是否收到了自己发送的报文。
  • 第三次握手:客户端向服务端发送报文(可以携带数据),服务器收到报文。此时服务端确认了双发的收发能力都没有问题。连接正式建立。

四次挥手

为什么要四次挥手?

举个例子:A 和 B 打电话,通话即将结束后。

  • 第一次挥手 : A 说"我没啥要说的了"
  • 第二次挥手 :B 回答"我知道了",但是 B 可能还会有要说的话,A 不能要求 B 跟着自己的节奏结束通话
  • 第三次挥手 :于是 B 可能又巴拉巴拉说了一通,最后 B 说"我说完了"
  • 第四次挥手 :A 回答"知道了",这样通话才算结束

为什么不能把服务器发送的 ACK 和 FIN 合并起来,变成三次挥手?

因为服务器收到客户端断开连接的请求时,可能还有一些数据没有发完,这时先回复 ACK,表示接收到了断开连接的请求。等到数据发完之后再发 FIN,断开服务器到客户端的数据传送

如果第二次挥手时服务器的 ACK 没有送达客户端,会怎样?

客户端没有收到 ACK 确认,会重新发送 FIN 请求。

为什么要等待 2MSL?

因为如果第四次挥手客户端发送的确定报文丢失, 服务器就收不到确认报文, 会触发超时重传机制, 服务器会重传第三次挥手的连接释放报文段. 若等待2MSL之后, 就能保证服务端收到了确认报文段, 关闭了连接.

为什么是两倍的最长报文寿命?

因为客户端第三次挥手发送确认报文段, 该报文段到服务器端最长时间是1MSL. 若丢失, 服务端在1MSL后超时重传第三次挥手的连接释放报文段, 这个报文段到客户端的最大时间又是1MSL. 所以要等待2MSL, 没有收到超时重传的连接释放报文段, 就说明确认报文段服务端收到了, 连接正确释放了.

相关推荐
ღ᭄ꦿ࿐Never say never꧂8 分钟前
微服务架构中的负载均衡与服务注册中心(Nacos)
java·spring boot·后端·spring cloud·微服务·架构·负载均衡
.生产的驴17 分钟前
SpringBoot 消息队列RabbitMQ 消息确认机制确保消息发送成功和失败 生产者确认
java·javascript·spring boot·后端·rabbitmq·负载均衡·java-rabbitmq
海里真的有鱼25 分钟前
Spring Boot 中整合 Kafka
后端
布瑞泽的童话31 分钟前
无需切换平台?TuneFree如何搜罗所有你爱的音乐
前端·vue.js·后端·开源
写bug写bug41 分钟前
6 种服务限流的实现方式
java·后端·微服务
离开地球表面_991 小时前
索引失效?查询结果不正确?原来都是隐式转换惹的祸
数据库·后端·mysql
Victor3561 小时前
Oracle(138)如何监控数据库性能?
后端
不修×蝙蝠2 小时前
eclipse使用 笔记02
前端·笔记·后端·eclipse
吃面不喝汤664 小时前
Flask + Swagger 完整指南:从安装到配置和注释
后端·python·flask
讓丄帝愛伱5 小时前
spring boot启动报错:so that it conforms to the canonical names requirements
java·spring boot·后端