前言
上篇文章介绍了 IP 地址的相关内容,那么电脑拿到了IP地址,我们就可以和目标计算机通信了吗?
其实,还有一个问题需要解决,就是网络通信本身是不可靠的,数据在网络中传输会遇到各种各样的问题。比如:数据包丢失,数据包重复,数据包乱序,数据包延迟等等,这些可能是由于网络设备故障,网络拥塞,传输线路损坏,不同的数据包可能走不同的路径到达目的地等原因造成的。
举个例子
想象你寄出 3 个快递包裹:
- 包裹1可能丢失了
- 包裹2和包裹3可能走了不同的路线
- 包裹3可能比包裹2先到达
- 某个包裹可能被寄出多次(系统重发)
前辈们遇到这些问题,他们是怎么解决的呢?他们提出了 TCP!
TCP
TCP 是基于 IP 协议的网络协议,它提供了:
- 确认机制
- 重传机制
- 排序机制
- 流量控制
- 拥塞控制
注意:IP 协议就像邮政系统的快递服务,而 TCP 协议就像是快递的跟踪、确认和重发机制。
下面简单了解下这些机制。
TCP 的可靠性机制
确认机制(Acknowledgment
- 接收方收到数据包后会发送确认包(ACK)给发送方
- 就像快递签收时的回执一样
- 例如:A 发送数据给 B,B 收到后会说"我收到了"
重传机制(Retransmission)
- 发送方发出数据后会启动一个定时器
- 如果在指定时间内没收到确认,就重新发送
- 就像快递丢失后,快递公司会重新发送一个包裹
- 例如:
- A 发送数据给 B
- 等待 3 秒没收到 B 的确认
- A 就会重新发送一次
排序机制(Sequencing)
- 每个数据包都有序号
- 接收方可以按序号重新排列乱序的数据包
- 就像给快递包裹标注 1/3、2/3、3/3
- 例如:
- 发送方:包裹1、包裹2、包裹3
- 接收方收到:包裹2、包裹3、包裹1
- 接收方会按序号重新排序
流量控制(Flow Control)
- 控制发送速率,防止接收方处理不过来
- 接收方会告诉发送方自己能处理多少数据
- 就像快递员会根据收件人的要求控制派件速度
- 例如:
- B 说"我一次最多能处理 1000 字节"
- A 就会控制发送速度,确保不超过这个限制
拥塞控制(Congestion Control)
- 根据网络状况调整发送速率
- 当检测到网络拥堵时会降低发送速率
- 就像遇到交通堵塞时,快递车会改道或放慢速度
- 例如:
- 如果发现数据包经常丢失
- TCP 会认为网络可能拥堵了
- 就会降低发送速度
校验和机制(Checksum)
- TCP 会为每个数据包计算一个校验和(类似数据的指纹)
- 发送方:计算数据的校验和,并随数据包一起发送
- 接收方:重新计算收到数据的校验和,与发送方的校验和比对
- 如果两个校验和不一致,说明数据在传输过程中被损坏了
- 接收方会丢弃这个包,不发送 ACK 确认
- 发送方因为没收到 ACK,就会重传这个包
一个有趣的例子:
- 小明给小红发了一句话:"你好啊"
- 发送时计算校验和是 100
- 传输过程中数据变成了"你坏啊"
- 小红收到后计算校验和是 90
- 发现校验和不一致(90 ≠ 100)
- 小红不回复"收到"
- 小明见没收到回复,就会重发"你好啊"
- 小红收到后计算校验和是 100
- 发现校验和一致(100 = 100)
- 小红回复"收到"
- 小明收到回复后,知道数据没问题,就停止重传
这些机制互相配合,共同保证了数据传输的可靠性。就像一个完善的快递系统,不仅要把包裹送到,还要保证按顺序送达、不丢失、不积压。
完整的例子
让我们用一个生动的"网络点餐"的例子来理解TCP的各种机制:
想象小明通过APP,在一家商店点了一个"双人套餐",包含:
- 一份牛肉面
- 一份炒饭
- 两杯奶茶
- 一份甜点
👉 确认机制(ACK)
- 小明每收到一个食物,都会在APP上点击"收到"
- 小明:收到牛肉面了!(ACK)
- 商家继续送下一样
👉排序机制(Sequencing)
- 商家给每个食物标上序号:#1面、#2饭、#3奶茶A、#4奶茶B、#5甜点
- 即使奶茶B先到,小明也知道这是第4个品项
- 可以正确记录哪些还没送到
👉重传机制(Retransmission)
- 如果其中一杯奶茶在路上撒了
- 商家15分钟后,还没收到这杯奶茶的"收到"确认
- 就会重新准备一杯送过去
👉流量控制(Flow Control)
- 小明说:"我家餐桌只能同时放3样食物"
- 商家就会先送3样,等小明吃掉一样,再送下一样
- 避免食物堆积,没地方放
👉拥塞控制(Congestion Control)
- 商家发现路上堵车了
- 就会改道或者放慢送餐速度
- 防止食物在路上放太久变凉
👉校验和机制(Checksum)
- 每样食物都有"完整性检查单"
- 比如:奶茶必须是满杯+带吸管+有封口
- 如果发现不完整(比如漏液),就不接收,要求重送
这样,即使遇到:
- 堵车(网络拥塞)
- 食物撒了(数据包丢失)
- 送错顺序(数据包乱序)
- 重复送了(数据包重复)
- 食物不完整(数据损坏)
TCP的各种机制都能保证小明最终完整收到他点的所有食物,而且是按照正确的顺序,保证食物的新鲜和完整。
如果数据包丢了,TCP会重发,如果数据包乱序了,TCP会排序,如果数据包重复了,TCP会丢弃,如果数据包延迟了,TCP会等待,如果数据包失真了,TCP会丢弃。
TCP对于数据包可能出现的情况,都做了预防,确保数据包的正确,可靠,有序,不丢失,不重复,不乱序,不延迟。
这下是不是就放心多了,纵使网络状态再差,数据包再乱,有TCP在,都没有问题。而HTTP就是基于TCP协议的,所以HTTP也是可靠的。
TCP的三次握手和四次挥手
握手和挥手的过程仅作了解,但如果最近有面试,这块还是好好学一学
三次握手 - 建立连接
就像两个人见面时的对话:
客户端:你好,在吗?
服务端:在的,你好!
客户端:好的,开始聊天吧
具体过程:
- 第一次握手
- 客户端发送 SYN 包
- 表示:我想和你建立连接
- 状态:SYN_SENT(客户端)
- 第二次握手
- 服务端返回 SYN + ACK 包
- 表示:我收到了,也想和你建立连接
- 状态:SYN_RECEIVED(服务端)
- 第三次握手
- 客户端发送 ACK 包
- 表示:好的,我们开始通信吧
- 状态:ESTABLISHED(双方)
为什么需要三次握手?
- 第一次:客户端确认自己能发能收
- 第二次:服务端确认自己能发能收,客户端能发
- 第三次:客户端确认服务端能发能收
SYN 表示这是一个连接请求的数据包,就想说“我想和你建立连接”
ACK 表示这是一个确认包,就想说“我收到了你的消息”
四次挥手 - 断开连接
就像两个人告别时的对话:
客户端:我说完了,准备走了
服务端:好的,我知道了,等我说完
服务端:我也说完了,可以走了
客户端:好的,拜拜
具体过程:
- 第一次挥手
- 客户端发送 FIN 包
- 表示:我没有数据要发了
- 状态:FIN_WAIT_1(客户端)
- 第二次挥手
- 服务端发送 ACK 包
- 表示:好的,我知道了,但我还有话要说
- 状态:CLOSE_WAIT(服务端)
- 第三次挥手
- 服务端发送 FIN 包
- 表示:我也说完了,可以断开了
- 状态:LAST_ACK(服务端)
- 第四次挥手
- 客户端发送 ACK 包
- 表示:好的,拜拜
- 状态:TIME_WAIT(客户端)
为什么需要四次挥手?
- TCP 是全双工的,两个方向的连接要单独关闭
- 一方关闭连接后,另一方可能还有数据要发送
- 所以每个方向都需要一个 FIN 和 ACK
FIN表示发送完成,但是还可以接受对方的数据
TCP讲了这么多,是不是头都要炸了,没关系,我们只需要记住两点:
- TCP 是可靠的,HTTP 是基于 TCP 的,所以 HTTP 是可靠的
- TCP 的连接建立和断开都是由浏览器自动处理的,我们不需要关心
下面我们看看TCP在实际前端开发中,有哪些应用场景
TCP 与前端开发的关系
HTTP 请求超时处理
因为 TCP 的重传机制,我们需要合理设置请求超时时间:
// 设置请求超时
fetch('https://api.example.com/data', {
signal: AbortSignal.timeout(5000) // 5秒超时
}).catch(error => {
if (error.name === 'AbortError') {
console.log('请求超时,可能是网络拥塞或服务器响应慢');
}
});
性能优化
TCP每次建立连接,都需要三次握手,所以每次建立连接,都需要消耗1.5个RTT(Round Trip Time)。那么理解 TCP 连接建立的开销,可以帮助优化性能:
- 域名收敛
- 减少不同域名的数量,复用 TCP 连接
- 但要平衡浏览器的并发连接数限制
- 资源合并
- 合并小文件减少 TCP 连接数
- 使用雪碧图、CSS/JS 打包等技术
- 预连接
<link rel="preconnect" href="https://api.example.com">
- 长连接优化 利用 TCP 的 keep-alive 特性优化请求:
// 使用 HTTP 长连接
fetch('https://api.example.com/data', {
headers: {
'Connection': 'keep-alive'
}
});
长连接什么意思:
- 长连接就是连接建立后,不立即断开,而是保持一段时间,这样可以减少连接建立和断开的时间开销。
- 比如你和快递员说,我需要寄快递,快递员说好的,然后你挂了电话,快递员继续接其他快递,但是你寄快递的请求还在,快递员需要处理完其他快递后,再回来处理你的快递。
- 这就是长连接,快递员处理完其他快递后,再回来处理你的快递。
这里把快递员比喻成:处理 HTTP 请求的 TCP 长链接
上面四个角度,都是基于避免TCP连接建立的开销,来优化性能的
总结
这篇我们了解 TCP 的一些基本概念:
- TCP 的可靠机制
- TCP 的三次握手和四次挥手
并且了解完 TCP 的三次握手后,从性能优化的角度,了解了下TCP 在前端开发中的应用场景
下篇文章,我们来看看基于 TCP 的 HTTP。这是我们前端开发打交道最多的网络协议