Skip to content

网络底层协议基础:前端开发者必知的网络知识

引言

你有没有想过,当我们在浏览器中输入一个网址按下回车后,背后发生了什么神奇的事情?作为前端开发者,了解网络底层协议不仅能帮助我们更好地理解网页加载过程,还能帮助我们诊断和解决各种网络相关的问题。今天,我们就一起来探索这个看不见但又无处不在的网络世界。

TCP 三次握手与四次挥手

想象一下,网络通信就像是两个人之间的对话。在开始深入交谈前,他们需要先确认双方都能听到对方的声音,这就是所谓的"握手"。而在对话结束时,双方也需要明确地告知对方"我说完了",这就是"挥手"。

TCP连接建立机制

TCP连接建立需要经过三步,也就是我们常说的"三次握手":

客户端                                服务器
   |                                    |
   |--------- SYN (seq=x) ---------->  |
   |                                    |
   |<------ SYN+ACK (seq=y,ack=x+1) -- |
   |                                    |
   |--------- ACK (ack=y+1) --------->  |
   |                                    |
  1. 第一次握手(SYN):客户端发送一个特殊的TCP包,标志位SYN=1,并生成一个随机序列号x。这就像说"嘿,你能听到我说话吗?"

  2. 第二次握手(SYN+ACK):服务器收到请求后,会发送自己的SYN包,同时确认客户端的SYN包。这就像回应"是的,我能听到你,你能听到我吗?"

  3. 第三次握手(ACK):客户端收到服务器的响应后,会再发送一个确认包,表示"是的,我们现在可以开始交流了"。

三次握手确保了双方都准备好收发数据,避免资源浪费。想象一下,如果只有两次握手,客户端发送了连接请求但自己却不知道服务器是否收到,就好比你喊了一声"喂",但不知道对方是否听到。

序列号与确认号的作用:

  • 序列号(SEQ):标识数据包的顺序,确保数据能正确重组
  • 确认号(ACK):告诉对方"我已收到你发的数据,期待下一个数据包"

超时重传策略:如果在预定时间内没收到响应,TCP会自动重发数据包,确保数据不会丢失。

四次挥手过程剖析

当通信结束时,TCP连接需要四步才能完全关闭,这就是"四次挥手":

客户端                                服务器
   |                                    |
   |--------- FIN (seq=x) ---------->  |
   |                                    |
   |<------ ACK (ack=x+1) ------------ |
   |                                    |
   |<------ FIN (seq=y) -------------- |
   |                                    |
   |--------- ACK (ack=y+1) --------->  |
   |                                    |
  1. 第一次挥手(FIN):客户端发送一个FIN包,表示"我没有数据要发送了"
  2. 第二次挥手(ACK):服务器收到FIN包,发送ACK确认,表示"好的,我知道你没数据发了,但我可能还有数据要发"
  3. 第三次挥手(FIN):服务器发送自己的FIN包,表示"我的数据也发完了"
  4. 第四次挥手(ACK):客户端收到服务器的FIN包,发送ACK确认,表示"好的,那我们就结束吧"

TIME_WAIT状态:客户端在发送最后一个ACK后,不会立即关闭,而是进入TIME_WAIT状态,等待2MSL(Maximum Segment Lifetime)的时间。这是为了确保最后一个ACK能成功送达,如果服务器没收到,会重发FIN包,客户端可以再次发送ACK。

半关闭状态是指连接的一方已关闭连接,但另一方仍可发送数据。这在某些需要发送完所有数据才关闭的场景很有用。

TCP可靠传输机制

TCP之所以能保证数据传输的可靠性,主要依靠以下机制:

  1. 序列号与确认应答:每个数据包都有唯一序列号,接收方通过确认应答告知发送方数据已收到

    发送方: 数据包1(seq=100) -> 数据包2(seq=200) -> 数据包3(seq=300)
    接收方: <- 确认(ack=400) // 表示已收到序列号前399的所有数据
  2. 超时重传算法:如果发送方在一定时间内没收到确认,会自动重发数据包

    javascript
    // 伪代码:超时重传
    function sendWithRetry(packet, maxRetries) {
      let retries = 0;
      while (retries < maxRetries) {
        send(packet);
        if (waitForAck(packet, timeout)) {
          return true; // 发送成功
        }
        retries++;
      }
      return false; // 发送失败
    }
  3. 快速重传与快速恢复:如果接收方收到了乱序的数据包,会立即发送对最后一个按序收到的数据包的重复确认,发送方收到3个重复确认后会立即重传丢失的数据包,不等待超时。

TCP流量控制

流量控制是为了防止发送方发送数据太快,导致接收方来不及处理:

  1. 滑动窗口原理:接收方通过窗口大小告诉发送方自己能处理多少数据,发送方据此控制发送速度

    接收方: 窗口大小=4000 ->  // 表示接收方还能接收4000字节的数据
    发送方: <- 数据(2000字节) // 发送方最多发送4000字节
    接收方: 窗口大小=2000 ->  // 接收方处理了一些数据,还能接收2000字节
  2. 零窗口与窗口探测:如果接收方发送窗口大小为0,发送方会暂停发送,并定期发送窗口探测包检查接收方是否已有空间接收数据

  3. 接收缓冲区管理:接收方通过缓冲区管理来控制窗口大小,避免数据溢出

TCP拥塞控制详解

拥塞控制是为了防止网络过载,导致数据包大量丢失:

  1. 慢启动与拥塞避免:TCP连接初始发送速率较低,然后逐渐增加,直到探测到网络拥塞

    拥塞窗口大小: 1 -> 2 -> 4 -> 8 -> 16 -> ... 
    // 当达到阈值或发生丢包时,进入拥塞避免阶段,拥塞窗口线性增长
    拥塞窗口大小: 16 -> 17 -> 18 -> ...
  2. 快速恢复算法:当检测到少量丢包时,TCP不会将拥塞窗口降为1,而是降低到一半,然后线性增长

  3. BBR等现代拥塞控制算法:传统的拥塞控制算法主要依靠丢包来检测拥塞,而BBR(Bottleneck Bandwidth and RTT)则通过测量网络带宽和延迟来更精确地控制发送速率,适合现代高速网络环境

TCP粘包问题

当我们使用TCP传输数据时,可能会遇到粘包问题,即多个数据包合并在一起:

发送: "Hello," "World!" 
接收: "Hello,World!"    // 粘包
或者
接收: "He" "llo,World!" // 拆包

产生原因

  1. TCP为提高传输效率,会将短时间内多个数据包合并发送(Nagle算法)
  2. 接收方的TCP缓冲区可能会缓存多个数据包,然后一次性交给应用层

解决方案

  1. 固定长度:每条消息固定长度
  2. 分隔符:在消息之间添加特殊分隔符
  3. 长度前缀:在每条消息前加上长度字段
    发送: [5]Hello[5]World
    接收: 根据长度字段正确解析出"Hello"和"World"

应用层协议设计考量:在设计应用层协议时应考虑到TCP粘包问题,合理设计消息边界

IP 与 DNS 解析过程

互联网是由无数台设备组成的网络,每台设备都需要有一个"地址"才能被找到,这就是IP地址的作用。而域名系统(DNS)则是将我们易于记忆的域名转换为IP地址的系统。

IP地址体系

IP地址是设备在网络中的唯一标识:

  1. IPv4与IPv6结构对比

    • IPv4:32位,表示为四组0-255的数字,如192.168.1.1
    • IPv6:128位,表示为8组十六进制数,如2001:0db8:85a3:0000:0000:8a2e:0370:7334

    IPv6的出现是为了解决IPv4地址耗尽的问题。

  2. 公网地址与私有地址

    • 公网地址:可直接访问互联网的IP地址
    • 私有地址:局域网内部使用的地址,如192.168.x.x10.x.x.x,无法直接访问互联网,需通过NAT(网络地址转换)技术
  3. CIDR与子网划分:CIDR(无类域间路由)通过子网掩码来划分IP地址段

    192.168.1.0/24 表示前24位是网络地址,剩余8位是主机地址
    共有 2^8 = 256 个IP地址(192.168.1.0 - 192.168.1.255)

IP路由基本原理

IP路由负责决定数据包如何从源地址到达目标地址:

  1. 路由表结构:设备通过路由表来决定数据包的下一跳去向

    目标网络      子网掩码        网关           接口
    0.0.0.0      0.0.0.0      192.168.1.1    eth0  # 默认路由
    192.168.1.0  255.255.255.0  0.0.0.0      eth0  # 直连网络
  2. 默认网关作用:当数据包的目标不在本地网络时,会发送到默认网关,由网关继续转发

  3. 路由协议简介:路由器之间通过路由协议交换路由信息,如OSPF、BGP等

DNS系统架构

DNS系统是分层次的:

  1. 根域名服务器:互联网最顶层的DNS服务器,知道所有顶级域名服务器的地址

  2. 顶级域名服务器:负责管理.com、.org、.net等顶级域名

  3. 权威域名服务器:负责特定域名的解析,如google.com的权威DNS服务器

  4. 递归解析服务器:通常由ISP提供,接收用户查询请求并递归查询其他DNS服务器

DNS递归查询流程

当我们在浏览器中输入www.example.com时,DNS解析大致经过以下步骤:

客户端 -> 本地DNS服务器 -> 根域名服务器 -> 顶级域名服务器(.com) -> 权威域名服务器(example.com)

具体流程:

  1. 缓存查询阶段:首先查询本地缓存、hosts文件和系统级DNS缓存

    javascript
    // 浏览器DNS缓存伪代码
    function resolveDomain(domain) {
      if (dnsCache[domain] && !isExpired(dnsCache[domain])) {
        return dnsCache[domain].ip;
      }
      return queryDNSServer(domain);
    }
  2. 根域名查询:如果本地没有缓存,则向根域名服务器查询

  3. 顶级域名查询:根服务器返回顶级域名服务器的地址,然后向顶级域名服务器查询

  4. 权威服务器查询:顶级域名服务器返回权威服务器的地址,最后向权威服务器查询获得最终的IP地址

DNS迭代查询机制

DNS查询有两种方式:递归查询和迭代查询:

  1. 与递归查询的区别

    • 递归查询:客户端只发出一次请求,由DNS服务器完成所有查询过程
    • 迭代查询:DNS服务器逐级查询,每次只告诉客户端下一步该查询哪个服务器
  2. 服务器间通信方式:DNS服务器之间通过UDP协议的53端口通信,但大数据包会切换到TCP

  3. 效率与负载分析:迭代查询减轻了DNS服务器的负担,但增加了客户端的复杂度

DNS记录类型详解

DNS系统存储了多种类型的记录:

  1. A记录与AAAA记录

    • A记录:域名到IPv4地址的映射,如example.com -> 93.184.216.34
    • AAAA记录:域名到IPv6地址的映射
  2. CNAME别名记录:将一个域名指向另一个域名

    blog.example.com CNAME www.example.com
  3. MX邮件交换记录:指定接收邮件的服务器

    example.com MX 10 mail.example.com
  4. TXT记录应用场景:用于存储文本信息,常用于SPF、DKIM等邮件验证

    example.com TXT "v=spf1 include:_spf.example.com ~all"

DNS优化策略

作为前端开发者,可以通过以下方式优化DNS解析:

  1. DNS预解析技术:提前解析可能需要访问的域名

    html
    <link rel="dns-prefetch" href="//example.com">
  2. 多CDN智能解析:根据用户地理位置解析到最近的CDN节点

  3. HTTPDNS应用场景:绕过运营商DNS,避免DNS劫持,常用于移动APP

    javascript
    // HTTPDNS伪代码
    async function httpDNS(domain) {
      const response = await fetch(`https://httpdns.example.com/resolve?domain=${domain}`);
      const data = await response.json();
      return data.ip;
    }

网络分层模型

网络通信是一个复杂的过程,为了便于理解和实现,专家们将其分为不同的层次。

OSI七层模型详解

OSI(开放系统互连)模型将网络通信分为7层:

  1. 物理层与数据链路层

    • 物理层:负责比特流的传输,如电缆、光纤
    • 数据链路层:负责物理介质上的可靠传输,如以太网、Wi-Fi
  2. 网络层与传输层

    • 网络层:负责网络寻址和路由,如IP协议
    • 传输层:负责端到端的数据传输,如TCP、UDP
  3. 会话层与表示层

    • 会话层:建立、管理和终止会话
    • 表示层:数据格式转换、加密解密
  4. 应用层协议分类:直接服务于用户应用的协议,如HTTP、FTP、SMTP等

OSI模型主要是理论模型,实际互联网使用的是TCP/IP模型。

TCP/IP四层模型

TCP/IP模型是实际使用的网络模型,分为4层:

  1. 与OSI模型的映射关系

    • 应用层(对应OSI的应用层、表示层、会话层)
    • 传输层(对应OSI的传输层)
    • 网络层(对应OSI的网络层)
    • 网络接口层(对应OSI的数据链路层和物理层)
  2. 每层主要协议

    • 应用层:HTTP、HTTPS、FTP、SMTP、DNS等
    • 传输层:TCP、UDP
    • 网络层:IP、ICMP、ARP
    • 网络接口层:以太网、Wi-Fi等
  3. 数据封装与解封装过程

    应用层数据 -> +TCP头 -> +IP头 -> +以太网头 -> 二进制数据 -> 传输 -> 解封装

网络协议交互图解

了解不同协议之间的交互有助于理解网络通信:

  1. HTTP请求完整网络栈流程

    浏览器 -> DNS解析 -> TCP三次握手 -> SSL/TLS握手(HTTPS) -> HTTP请求 -> 服务器处理 -> HTTP响应 -> 数据解析与渲染 -> TCP四次挥手
  2. WebSocket连接建立过程

    HTTP升级请求 -> 101状态码响应 -> WebSocket连接建立 -> 全双工通信
  3. HTTPS握手全链路分析

    TCP三次握手 -> Client Hello(支持的加密算法) -> Server Hello(选择的加密算法) -> 服务器证书 -> 密钥交换 -> 加密通信开始

UDP协议与应用场景

UDP(用户数据报协议)是一种简单、无连接的传输层协议,适用于对实时性要求高但允许少量丢包的场景。

UDP特性与TCP对比

UDP与TCP的主要区别:

  1. 无连接设计原理:UDP不需要像TCP那样建立连接,直接发送数据

    TCP: 三次握手 -> 数据传输 -> 四次挥手
    UDP: 直接发送数据
  2. 高效传输优势:没有连接开销、确认应答和拥塞控制,传输延迟更低

    TCP头部: 20-60字节
    UDP头部: 8字节
  3. 适用场景分析

    • DNS查询:简单、快速的查询
    • 视频直播:允许少量丢包,重要的是实时性
    • 在线游戏:需要低延迟
    • IoT设备:资源有限的设备

可靠UDP实现方案

虽然UDP本身不可靠,但可以在应用层添加可靠性:

  1. 应用层重传机制:应用自己实现确认和重传

    javascript
    // 简化的应用层重传机制
    function sendWithReliability(data, maxRetries) {
      let retries = 0;
      const messageId = generateId();
      
      function send() {
        udpSend({ id: messageId, data: data });
        setTimeout(() => {
          if (!ackReceived[messageId] && retries < maxRetries) {
            retries++;
            send();
          }
        }, TIMEOUT);
      }
      
      send();
    }
    
    // 接收确认
    function onReceiveAck(ack) {
      ackReceived[ack.id] = true;
    }
  2. QUIC协议可靠性设计:Google开发的基于UDP的传输协议,实现了类似TCP的可靠性,同时保持UDP的低延迟特性

  3. 实时媒体传输优化:如RTP(实时传输协议)、RTCP(实时传输控制协议)

UDP数据报结构

UDP数据报结构简单:

  1. 首部字段分析:只有源端口、目标端口、长度和校验和四个字段

    0      15 16    31
    +--------+--------+
    | 源端口 | 目标端口 |
    +--------+--------+
    | 长度   | 校验和  |
    +--------+--------+
    |     数据部分     |
    +--------+--------+
  2. 最大传输单元(MTU):物理网络的最大数据包大小,通常为1500字节

    • 如果UDP数据报超过MTU,IP层会进行分片
  3. 分片与重组机制:当UDP数据报需要分片时,由IP层负责分片和重组

常见网络性能指标

网络性能对前端应用的用户体验有重大影响,了解这些指标有助于诊断和优化性能问题。

延迟(Latency)测量

网络延迟是指数据从源到目的地所需的时间:

  1. RTT(往返时间)计算:从发送数据到收到响应的时间

    javascript
    // 测量RTT的简化代码
    async function measureRTT(url) {
      const start = performance.now();
      await fetch(url);
      const end = performance.now();
      return end - start; // RTT in milliseconds
    }
  2. 网络路径追踪:使用traceroute/tracert工具跟踪数据包的路径和每一跳的延迟

  3. 前端性能监控指标

    • DNS解析时间
    • TCP连接时间
    • TLS握手时间
    • TTFB(Time To First Byte)
    • 资源加载时间

带宽与吞吐量

带宽是指网络的最大传输能力,吞吐量是实际传输速率:

  1. 理论带宽与实际吞吐量

    • 理论带宽:如100Mbps的网络连接
    • 实际吞吐量:受多种因素影响,通常低于理论带宽
  2. 影响因素分析

    • 网络拥塞
    • 服务器负载
    • 协议开销
    • 距离和延迟
  3. 性能测试方法

    • 速度测试网站(如speedtest.net)
    • 下载测试文件
    • 网络性能监控工具

网络质量与丢包率

丢包率是网络质量的重要指标:

  1. 丢包原因分析

    • 网络拥塞
    • 硬件故障
    • 信号干扰
    • 缓冲区溢出
  2. 质量评估标准

    • 优秀:丢包率<0.1%
    • 良好:丢包率<1%
    • 一般:丢包率1%-2.5%
    • 较差:丢包率>2.5%
  3. 补偿策略与算法

    • 前向纠错(FEC):添加冗余数据,即使部分丢包也能恢复
    • 自适应比特率:根据网络质量调整数据传输速率
    • 丢包重传:检测到丢包后请求重传

总结与前端实践

作为前端开发者,了解网络协议可以帮助我们:

  1. 更好地优化网站性能:

    • 减少DNS查询时间
    • 复用TCP连接
    • 合理设置资源缓存
    • 使用HTTP/2或HTTP/3
  2. 更精确地诊断网络问题:

    • 区分前端问题、网络问题和后端问题
    • 使用浏览器开发者工具分析网络请求
    • 理解常见错误状态码的含义
  3. 设计更高效的前端应用:

    • 选择合适的数据传输策略(REST API、WebSocket、SSE等)
    • 实现智能的重试机制
    • 优化首屏加载时间

实用工具推荐

  1. 网络抓包工具:Wireshark、Fiddler、Charles
  2. 浏览器开发者工具的Network面板
  3. 命令行工具:ping、traceroute、nslookup

学习资源推荐

  1. MDN HTTP 文档
  2. 《图解HTTP》和《图解TCP/IP》
  3. 性能优化指南

网络知识看似复杂,但掌握了基础后,你会发现它对前端开发的重要性。希望这篇文章能帮助你理解网络协议的基础知识,为你的前端开发之路添砖加瓦。

注:本文档会持续更新,欢迎关注!

用❤️分享前端技术 fedev.wiki