Files

5.0 KiB
Raw Permalink Blame History

id, title, type, domain, stack, tags, level, prerequisites, est_hours, related, week, order
id title type domain stack tags level prerequisites est_hours related week order
cs.network-os.http-tcp 网络基础:从输入 URL 到页面展示、TCP 握手挥手与粘包、HTTP/1.1 / 2 / 3、HTTPS、长连接与 SSE lesson cs
network-os
TCP
三次握手
四次挥手
TIME_WAIT
粘包
HTTP/2
HTTP/3
HTTPS
TLS
DNS
keep-alive
SSE
WebSocket
basic
3
cs.network-os.os-basics
cs.network-os.scenario.connection-troubleshooting
devops.nginx.https-stream-limit
14
16
1

网络基础:从输入 URL 到页面展示、TCP 握手挥手与粘包、HTTP/1.1 / 2 / 3、HTTPS、长连接与 SSE

BOSS直聘 2026-10-23 的调研中有岗位提到计算机基础(网络 / 操作系统)。前端出身的你对 HTTP 和浏览器很熟,这里补齐 TCP 和服务端视角。

解释说明

从输入 URL 到页面展示(经典题,按层回答)

  1. DNS 解析:浏览器缓存 → 系统缓存 / hosts → 递归解析器 → 根 / 顶级域 / 权威服务器,得到 IP。
  2. 建立连接:TCP 三次握手;HTTPS 再进行 TLS 握手(TLS 1.3 为 1 个往返);HTTP/3 基于 QUIC(UDP),连接和加密握手合并。
  3. 发送请求,经过 CDN / 负载均衡 / 网关到达应用服务器。
  4. 服务器处理并响应。
  5. 浏览器渲染:解析 HTML 构建 DOM、CSS 构建 CSSOM、合成渲染树、布局、绘制、合成;遇到同步脚本会阻塞解析。

TCP

  • 三次握手:SYN → SYN+ACK → ACK。为什么是三次:双方都要确认“对方能收到我发的”,并同步初始序号;两次无法防止过期的连接请求被误建立。
  • 四次挥手:FIN → ACK → FIN → ACK(因为一方关闭发送后,另一方可能还有数据要发)。
  • TIME_WAIT:主动关闭方等待 2MSL,确保最后的 ACK 能被重传、旧连接的报文消失。短连接高并发时,主动关闭方(常常是作为客户端的服务)会积累大量 TIME_WAIT —— 解决办法是连接复用(连接池、keep-alive),而不是盲目修改内核参数。
  • 可靠传输:序号与确认、超时重传、滑动窗口(流量控制)、拥塞控制(慢启动、拥塞避免)。
  • 字节流,没有消息边界:“粘包 / 拆包”是应用层没有定义边界导致的,要用分隔符或长度前缀解决(示例中实际演示)。

HTTP 演进

版本 关键点
HTTP/1.1 持久连接(keep-alive);一个连接同一时间只能处理一个请求(队头阻塞),浏览器对同域开多个连接
HTTP/2 二进制分帧、多路复用(一个连接并发多个流)、头部压缩;TCP 层的丢包仍会阻塞所有流
HTTP/3 基于 QUIC(UDP),解决 TCP 层队头阻塞,连接迁移(切换网络不断开)

HTTPS

TLS 用非对称加密(或密钥交换)协商出会话密钥,再用对称加密传输数据;证书由 CA 签名,客户端校验证书链和域名,防止中间人。

长连接推送:轮询、SSE、WebSocket

方式 方向 适合
轮询 客户端拉 简单、低频
SSE 服务器 → 客户端,基于普通 HTTP,文本事件,浏览器自动重连 大模型流式输出(OpenAI 等接口都用它)
WebSocket 双向,独立协议 聊天、协同编辑、实时语音

案例

AI 对话接口用 SSE 流式返回:经过 Nginx 时要关闭缓冲;HTTP/1.1 下浏览器对同一域名的并发连接数有限(通常 6 个),用户开多个标签页时 SSE 连接可能占满,HTTP/2 下多路复用没有这个问题。

代码示例

examples/tcp-basics(只用标准库):

$ python framing.py
发送方 send 了 3 次(每次 4 字节),接收方一次 recv 收到 12 字节 → 三条消息粘在一起,边界丢失
长度前缀分帧:每次只读 3 字节,仍然还原出 3 条完整消息:['第0条', '第1条', '第2条']

增量解码器(test_framing.py 用 100 种随机切分验证):

def feed(self, data: bytes) -> list[dict]:
    self.buf += data
    out = []
    while len(self.buf) >= HEADER.size:
        (n,) = HEADER.unpack_from(self.buf)
        if n > self.max_frame:
            raise ValueError("帧长度超过上限")
        if len(self.buf) < HEADER.size + n:
            break
        out.append(json.loads(bytes(self.buf[HEADER.size:HEADER.size + n])))
        del self.buf[:HEADER.size + n]
    return out

易错点

  • 认为一次 send 对应一次 recv。
  • 为了解决 TIME_WAIT 乱改内核参数,而不是复用连接。
  • 把 HTTP/2 的多路复用和 HTTP/1.1 的管线化混为一谈。
  • 不设置超时(连接超时、读超时),上游挂起时线程 / 协程被耗尽。

面试常问

  • 从输入 URL 到页面展示发生了什么?
  • 为什么 TCP 握手是三次、挥手是四次?TIME_WAIT 的作用?
  • HTTP/1.1、2、3 的区别?
  • SSE 和 WebSocket 的区别?大模型流式输出为什么常用 SSE?