从 TCP 到 WebSocket:一次搞懂网络通信的三层演进
引言
在现代应用开发中,网络通信是绕不开的核心议题。无论是构建传统的 Web 应用,还是开发需要实时交互的系统(如在线协作工具、金融行情推送、多人游戏),我们总会与 TCP、HTTP、WebSocket 这些名词打交道。它们之间究竟是何种关系?为何有了 HTTP 之后还需要 WebSocket?
本文旨在从开发者的视角,深入剖析这三者之间的技术关联与演进逻辑。我们将逐层递进,从底层的传输协议到上层的应用规范,清晰地揭示它们各自的职责、设计哲学以及在不同场景下的技术选型考量。
第一层:基石 - TCP,一切可靠通信的源头
TCP (Transmission Control Protocol,传输控制协议) 是网络协议栈中的传输层协议。它的核心使命只有一个:提供一个可靠的、面向连接的、基于字节流的端到端通信服务。可以将其理解为网络世界的“可靠管道”,后续的应用层协议(如HTTP和WebSocket)都构建在这条管道之上。
TCP 的核心机制
为了实现“可靠性”,TCP 设计了几个关键机制:
三次握手 (Three-Way Handshake):在数据传输前,客户端与服务器必须建立连接。
- SYN (Synchronize Sequence Numbers): 客户端发送一个SYN包,请求建立连接并同步初始序列号。
- SYN-ACK: 服务器收到后,回复一个SYN-ACK包,确认客户端的请求,并发送自己的初始序列号。
- ACK: 客户端收到后,再发送一个ACK包,表示确认收到服务器的同步信号。至此,一个双向可靠的连接建立完成。
这个过程确保了双方都具备收发数据的能力,并就初始序列号达成了一致,为后续的数据包排序和确认奠定了基础。
可靠数据传输:
- 序列号 (Sequence Number): TCP 将发送的数据分割成块,并为每个块分配一个唯一的序列号。
- 确认应答 (Acknowledgement, ACK): 接收方每收到一个数据块,都会发送一个ACK包,告知发送方“我已经收到了序列号为X的数据”。
- 超时重传 (Timeout Retransmission): 如果发送方在一定时间内没有收到某个数据块的ACK,它会认为该数据包丢失,并重新发送。
流量控制 (Flow Control):通过滑动窗口 (Sliding Window) 机制,接收方可以告知发送方自己还有多少缓冲区空间可以接收数据,防止发送方过快发送数据导致接收方缓冲区溢出。
拥塞控制 (Congestion Control):通过慢启动、拥塞避免等算法,TCP 能够感知网络拥堵状况,动态调整发送速率,避免造成网络瘫痪。
开发者视角:我们通常不直接操作 TCP。操作系统内核的网络栈已经为我们处理了这一切复杂性。当我们使用高级语言(如Java, Go, Python)创建一个 Socket 时,我们得到的实际上就是一个封装好的 TCP 通道。我们只管往里写数据(write)和从里面读数据(read),可靠性由底层 TCP 保证。
第二层:规约 - HTTP,构建 Web 世界的无状态契约
HTTP (HyperText Transfer Protocol) 是一个应用层协议,它构建于 TCP 之上。它定义了客户端(通常是浏览器)和服务器之间请求和响应的格式与规则。
HTTP 的核心特性
请求-响应模型 (Request-Response Model):通信严格由客户端发起。客户端发送一个请求,服务器返回一个响应。服务器不能主动向客户端推送信息。
无状态 (Stateless):每个 HTTP 请求都是独立的。服务器不会记录前一个请求的任何信息。这种设计简化了服务器的实现,使其易于水平扩展。但对于需要维持登录状态等场景,则必须借助外部机制,如 Cookies 和 Session。
连接管理的演进
这是理解 HTTP 性能瓶颈与优化的关键:
HTTP/1.0 - 短连接:
最早的设计是“一次请求,一次连接”。每个 HTTP 请求都需要经历一次完整的TCP握手 -> 数据传输 -> TCP挥手流程。当一个网页包含大量图片、CSS、JS文件时,这种模式会产生巨大的连接建立开销。HTTP/1.1 - 持久连接 (Persistent Connection / Keep-Alive):
为了解决短连接的低效问题,HTTP/1.1 默认启用持久连接。客户端和服务器在完成一次请求-响应后,不会立即关闭 TCP 连接,而是会保持一段时间(由Keep-Alive-Timeout控制)。后续的请求可以复用这个已建立的 TCP 通道,从而省去了多次握手的开销。请求头示例:
GET /style.css HTTP/1.1
Host: example.com
Connection: keep-alive
Connection: keep-alive明确告知服务器希望保持连接。尽管这是 HTTP/1.1 的默认行为,但显式声明是一种良好实践。开发者视角:持久连接极大地提升了 Web 页面加载性能。但其本质并未改变——依然是客户端发起,服务器响应。对于需要服务器主动、低延迟推送数据的场景(如聊天室),客户端只能通过轮询 (Polling) 或长轮询 (Long Polling) 等方式模拟,但这会带来延迟和资源浪费。
第三层:进化 - WebSocket,打破请求-响应枷锁的全双工通道
WebSocket 协议同样是构建于 TCP 之上的应用层协议。它的出现,正是为了解决 HTTP 在实时通信领域的根本性缺陷。
WebSocket 的诞生:协议升级
WebSocket 的巧妙之处在于,它通过一次标准的 HTTP 请求来完成“握手”和“协议升级”。
客户端发起升级请求: 客户端发送一个特殊的 HTTP GET 请求。
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Upgrade: websocket: 表明客户端希望将协议从 HTTP 升级到 WebSocket。Connection: Upgrade: 一个标准的 HTTP/1.1 头,配合Upgrade使用。Sec-WebSocket-Key: 一个 Base64 编码的随机字符串,用于简单的握手认证,防止意外的或恶意的连接。
服务器响应升级: 如果服务器支持 WebSocket,它会返回状态码
101 Switching Protocols。HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Accept: 服务器将客户端的Sec-WebSocket-Key与一个固定的“魔术字符串” (258EAFA5-E914-47DA-95CA-C5AB0DC85B11) 拼接后,计算 SHA-1 哈希,再进行 Base64 编码得到。客户端会验证此值,以确认服务器确实理解 WebSocket 协议。
握手成功后,这个底层的 TCP 连接就不再用于传输 HTTP 数据了。它被“劫持”并升级为一个全双工、持久化的 WebSocket 通道。
WebSocket 的核心优势
全双工通信 (Full-Duplex): 一旦连接建立,客户端和服务器的地位完全平等。任何一方都可以随时向对方主动发送数据,无需等待对方的请求。
持久化连接: 连接会一直保持,直到某一方明确地关闭它。这避免了反复建立连接的开销,并保证了通信的即时性。
更小的数据开销: WebSocket 的数据帧(Frame)格式非常轻量。每个数据帧只有很小的头部(2-10字节),相比每次请求都携带大量冗余信息的 HTTP 头部,其传输开销极低。
JavaScript 客户端代码示例:
// 1. 创建 WebSocket 连接 (URL 使用 ws:// 或 wss://)
const socket = new WebSocket('wss://example.com/chat');
// 2. 监听连接打开事件
socket.onopen = function(event) {
console.log('Connection established!');
// 连接建立后,可以立即发送消息
socket.send('Hello Server!');
};
// 3. 监听消息接收事件
socket.onmessage = function(event) {
// event.data 包含了从服务器接收到的数据
console.log('Message from server: ', event.data);
};
// 4. 监听连接关闭事件
socket.onclose = function(event) {
if (event.wasClean) {
console.log(`Connection closed cleanly, code=${event.code}, reason=${event.reason}`);
} else {
console.error('Connection died');
}
};
// 5. 监听错误事件
socket.onerror = function(error) {
console.error(`[error] ${error.message}`);
};
总结与对比
| 特性 | TCP | HTTP/1.1 | WebSocket |
|---|---|---|---|
| 协议层级 | 传输层 | 应用层 | 应用层 |
| 底层依赖 | IP 协议 | TCP | TCP |
| 连接模型 | 面向连接 | 持久连接 (但逻辑上无状态) | 持久化全双工连接 |
| 通信模式 | 全双工字节流 | 请求-响应 (客户端主导) | 全双工 (双向对等) |
| 数据开销 | 极低 (仅 TCP 头) | 较高 (每次请求都有冗余头) | 极低 (轻量级数据帧) |
| 适用场景 | 任何需要可靠传输的底层服务 | Web 页面浏览、API 调用、文件下载 | 实时聊天、在线游戏、数据推送、协同编辑 |
结语
理解 TCP、HTTP 和 WebSocket 的关系,本质上是理解网络通信抽象层次的演进过程:
- TCP 是坚实可靠的地基,它不关心上层应用在“说什么”,只负责把话“可靠地”送到。
- HTTP 是建立在地基之上的标准化“问答亭”,规矩森严(一问一答),通用性极强,构建了整个 Web 世界。
- WebSocket 则是对“问答亭”的一次革命性改造,它保留了地基,但拆掉了亭子,换上了一部“对讲机”,实现了真正自由、高效的实时对话。
作为开发者,在进行技术选型时,清晰地认知每一层协议的边界与能力,才能为特定的业务场景选择最恰当的“通信工具”,从而构建出高效、稳健的应用程序。
关注 【松哥AI自动化】 公众号,每周获取深度技术解析,从源码角度彻底理解各种工具的实现原理。更重要的是,遇到技术难题时,直接联系我!我会根据你的具体情况,提供最适合的解决方案和技术指导。
上期回顾:(Go+WebSocket+Chrome Extension:基于真实浏览器环境的 cf_clearance 自动化获取方案)
从 TCP 到 WebSocket:一次搞懂网络通信的三层演进的更多相关文章
- 一文搞懂如何使用Node.js进行TCP网络通信
摘要: 网络是通信互联的基础,Node.js提供了net.http.dgram等模块,分别用来实现TCP.HTTP.UDP的通信,本文主要对使用Node.js的TCP通信部份进行实践记录. 本文分享自 ...
- 真正“搞”懂HTTP协议05之What's HTTP?
前面几篇文章,我从纵向的空间到横向的时间,再到一个具体的小栗子,可以说是全方位,无死角的覆盖了HTTP的大部分基本框架,但是我聊的都太宽泛了,很多内容都是一笔带过,再加上一句后面再说就草草结束了.并且 ...
- 真正“搞”懂http协议01—背景故事
去年读了<图解HTTP>.<图解TCP/IP>以及<图解网络硬件>但是读了之后并没有什么深刻的印象,只是有了一层模糊的脉络,刚好最近又接触了一些有关http的相关内 ...
- 搞懂分布式技术9:Nginx负载均衡原理与实践
搞懂分布式技术9:Nginx负载均衡原理与实践 本篇摘自<亿级流量网站架构核心技术>第二章 Nginx负载均衡与反向代理 部分内容. 当我们的应用单实例不能支撑用户请求时,此时就需要扩容, ...
- 搞懂分布式技术10:LVS实现负载均衡的原理与实践
搞懂分布式技术10:LVS实现负载均衡的原理与实践 浅析负载均衡及LVS实现 原创: fireflyc 写程序的康德 2017-09-19 负载均衡 负载均衡(Load Balance,缩写LB)是一 ...
- 搞懂分布式技术5:Zookeeper的配置与集群管理实战
搞懂分布式技术5:Zookeeper的配置与集群管理实战 4.1 配置文件 ZooKeeper安装好之后,在安装目录的conf文件夹下可以找到一个名为“zoo_sample.cfg”的文件,是ZooK ...
- Web端即时通讯基础知识补课:一文搞懂跨域的所有问题!
本文原作者: Wizey,作者博客:http://wenshixin.gitee.io,即时通讯网收录时有改动,感谢原作者的无私分享. 1.引言 典型的Web端即时通讯技术应用场景,主要有以下两种形式 ...
- 彻底搞懂 etcd 系列文章(一):初识 etcd
0 专辑概述 etcd 是云原生架构中重要的基础组件,由 CNCF 孵化托管.etcd 在微服务和 Kubernates 集群中不仅可以作为服务注册与发现,还可以作为 key-value 存储的中间件 ...
- 搞懂ELK并不是一件特别难的事(ELK)
本篇文章主要介绍elk的一些框架组成,原理和实践,采用的ELK本版为7.7.0版本 一.ELK介绍 1.1.ELK简介 ELK是Elasticsearch.Logstash.Kibana三大开源框架首 ...
- 真正“搞”懂HTTP协议02之空间穿梭
时隔四年,这个系列鸽了四年,我终于觉得我可以按照自己的思路和想法把这个系列完整的表达出来了. 想起四年前,那时候还是2018年的六月份,那时候我还工作不到两年,那时候我翻译了RFC2616的部分内容, ...
随机推荐
- Docker部署RocketMQ(JDK11)
说起微服务,不谈容器,不谈云,那还谈个啥?容器中又以Docker最为流行,那么我们今天就来实践下容器化微服务,然后顺带解决下各种疑难杂症. 环境: Idea2019.03/Gradle6.0.1/JD ...
- leetcode每日一题:转换二维数组
题目 2610. 转换二维数组 给你一个整数数组 nums .请你创建一个满足以下条件的二维数组: 二维数组应该 只 包含数组 nums 中的元素. 二维数组中的每一行都包含 不同 的整数. 二维数组 ...
- Linux权限之基础权限
介绍 Linux是多用户的操作系统,允许多个用户同时登录和工作,Linux权限是操作系统用来限制不同用户对资源的访问机制.这里暂且将Linux的权限分为三类: 基本权限:给文件和目录的所属者.所属组. ...
- unigui的demo-\Demos\Desktop\DBAppDemo\SimpleDemo.dproj【11】
这个demo很简单. 一个客户表,还有一个票据主从表. 看程序界面: 包括数据提交,彻头彻尾的c/s程序.你完全按照传统的C/S程序模式做开发就可.好处是效率.效率.还是效率! 你还有什么不满意!如果 ...
- MySQL 中 varchar 和 char 有什么区别?
MySQL 中 varchar 和 char 的区别 在 MySQL 中,VARCHAR 和 CHAR 是两种常用的字符串类型,它们在存储方式.长度限制和使用场景等方面存在显著区别. 1. 定义与存储 ...
- kette介绍-Step之Table input
表输入(Table Input)介绍: Table input用于将数据源的数据加载到Kettle转换中的行集,可以说是数 据从持久化到内存的一种加载变换,故名为输入.加载内部过程,就是通过JDBC ...
- Sentinel源码—5.FlowSlot借鉴Guava的限流算法
大纲 1.Guava提供的RateLimiter限流使用示例 2.Guava提供的RateLimiter简介与设计 3.继承RateLimiter的SmoothBursty源码 4.继承RateLim ...
- 🚀 放弃 Oh-My-Posh,转而手搓 FastPrompt,打造快速高效的命令提示
「够用.够快.够自由」才是我心目中的终端提示符. 一个开发者的烦恼 每天打开 PowerShell,等待提示符加载完毕,我的内心就像在等待一个磨蹭的同事. 我用的是 Windows Terminal ...
- K8s Pod 控制器介绍及应用示例
Kubernetes 官方文档:Pod 控制器 Pod控制器介绍 Pod是kubernetes的最小管理单元,在kubernetes中,按照pod的创建方式可以将其分为两类: 自主式pod:kuber ...
- MCP协议Streamable HTTP
一.概述 2025 年 3 月 26 日,模型上下文协议(Model Context Protocol,简称 MCP)引入了一项关键更新:用 Streamable HTTP 替代原先的 HTTP + ...