Hi!这里是 JustHappy 这是专为编程初学者准备的专栏。这次我们来"上网",但是互联网不是网页,也不只是 HTTP,它本质上是不同机器上的程序按规则交换数据。看懂客户端、服务器、协议、操作系统、网卡这条链,再分清 HTTP 的"一问一答"和 WebSocket 的"持续通信",你的网络世界观才算真正搭起来。

上一篇我们讲到这里:
代码会执行、会发起 IO、硬件会工作、中断会通知CPU、操作系统会处理。
最后程序收到事件,再继续执行。
写到这里,很多人会自然冒出一个新问题:
如果 IO 不只是读本地文件,不只是等键盘输入,而是要去另一台电脑拿数据呢?
比如:
- 浏览器为什么能打开网页?
fetch('/api/user')为什么能拿到服务器返回的数据?- 聊天软件为什么能收到别人刚发来的消息?
- 为什么有的是"请求一次,返回一次",有的却能一直保持连接?
这时候你就会撞上一个更大的概念:
互联网。
但"互联网"这三个字,很多人其实一直都理解得很糊。
有人以为互联网就是浏览器。
有人以为 HTTP 就等于网络。
有人以为服务器就是"一台更厉害的电脑"。
还有人把接口、协议、连接、端口、Socket 全混在一起。
所以这一篇我们不从百科定义讲起。
我们只做一件事:
把"两台程序怎么隔着网络说话"这件事,一路讲通。
从上一篇接过来:网络其实是"更远一点的 IO"
上一篇我们讲过,程序发起一次 IO,大致会经历这条路:
现在如果你写:
js
fetch('/api/user')
它本质上还是 IO。
只不过这次不是让硬盘去读文件,而是让网卡把请求发出去,再把对方机器的数据收回来。
所以你可以把网络请求理解成:
程序通过操作系统和网卡,向另一台机器发消息,并等待对方回应。
注意这里已经出现了两个很重要的角色:
- 发消息的人
- 回消息的人
这也就是后面所有网络编程里最核心的两个身份:
客户端 和 服务器。
客户端和服务器,到底谁是谁
很多人第一次学网络的时候,会把"客户端"和"服务器"理解成两台不同档次的电脑。
好像客户端就是你手里的普通电脑、手机。
服务器就是机房里那种特别贵、特别强的大机器。
这样理解不算全错,但不够关键。
因为在开发里,我们更关心的其实不是"机器强不强",而是:
谁在发请求,谁在接请求。
你可以先想象一个很简单的场景:
一家奶茶店开了一个窗口。
外面站着很多顾客,大家都会来点单。
窗口里面有店员,专门负责接单、做单、把结果递出来。
这时候:
- 顾客像客户端
- 店员和窗口后的服务程序像服务器
客户端的特点是:它主动来发起请求。
服务器的特点是:它一直开着,专门等很多人来请求它。
所以更贴近真实世界的画面其实不是:
一台服务器只服务一台电脑。
而是:
很多客户端,同时去访问同一个服务器。
比如你打开掘金的时候,你是一个客户端。
我打开掘金的时候,我也是一个客户端。
还有成千上万别的用户,也都是客户端。
但我们访问的,可能是同一组服务器程序。
也就是说,服务器最重要的特征不是"它是一台很强的电脑",而是:
它是一个长期在线、专门接收请求、并且能同时服务很多客户端的程序。
所以你可以这么记:
- 客户端:发请求的人
- 服务器:接请求并返回结果的人
至于它们跑在什么机器上、机器有多强,那是后面的事。

什么是通信?为什么程序一定要通信?
说白了,通信就是:
一边发送信息,另一边接收信息。
人和人聊天,是通信。
浏览器访问服务器,也是通信。
你的后端程序去查数据库,本质上还是通信。
所以在计算机世界里,通信其实没那么玄乎。
它本质上就是:
一个程序把数据发出去,另一个程序把数据收进来。
那为什么一定要通信?
因为大多数程序都不是活在真空里的。
比如浏览器想显示用户信息,它自己并不知道你的头像、昵称、订单数据长什么样,它得去问服务器要。
后端程序想登录校验,它自己也不一定知道用户密码存在哪,它还得去问数据库。
你会发现:程序之所以要通信,是因为它需要从外部拿数据,或者把数据交给外部。
也就是说,只要一个程序没办法靠自己独立完成事情,它就必须和别的程序通信。
通信时,真正传来传去的是什么?
很多初学者第一次学网络时,很容易脑补成:
"我的电脑连到了对方电脑。"
这话不能说错,但不够准。
开发者脑子里更应该有的画面其实是:
我这台机器里的一个程序,正在和另一台机器里的一个程序交换数据。
比如你写:
JavaScript
login({
username: 'zhangsan',
password: '123456'
})
真正发生的不是"JavaScript 飞到了服务器那边"。
而是:
- 浏览器里的程序先组织出一段请求数据
- 操作系统把这段数据交给网卡发出去
- 网络把这段数据送到服务器那边
- 服务器程序收到后,看懂这段数据,处理完再回一段结果
- 结果再沿着网络一路返回给浏览器
所以网络世界里,真正传来传去的,不是代码,而是:数据。
代码是本地执行的。 通信传输的是请求数据、响应数据、消息数据、文件数据这些内容。
所以你完全可以把网络通信粗暴理解成一句话:
程序 A 发一段数据给程序 B,程序 B 收到以后再回一段数据。
程序之间靠什么通信?
既然通信本质上是"传数据",那下一步就很好理解了:
想把数据从一边送到另一边,就必须有"介质"。
就像人说话可以靠空气传播声音,写信可以靠纸张和邮递员,程序通信也得有自己的通道。
常见的通信介质,大概可以先这样理解:
1. 电信号 / 网线

如果两台机器通过网线连在一起,数据本质上会变成电信号,在网线里传输。
2. 无线电波 / Wi-Fi

如果你用的是 Wi-Fi,那数据本质上是通过无线电波在传。
所以你手机、笔记本没插线也能上网,本质上不是"没有介质",而是介质换成了无线信号。
3. 光信号 / 光纤

更远距离、更高速的网络传输,很多时候会用光纤。
这时候数据会变成光信号,在光纤里传输。
4. 本机内部通信介质
有些程序通信甚至不需要出机器。
比如浏览器和操作系统、进程和进程之间,也可以通过内存、管道、本地 socket 这种方式交换数据。

所以你可以发现:
通信并不等于上互联网。
只要两个程序之间要交换数据,就已经进入"通信"这件事了。
区别只在于:它们是通过什么介质、隔多远、按什么规则在传。
所以"网络"到底是什么?
现在你就可以把前面这些东西收拢起来了。
所谓网络,说白了就是:
让不同设备上的程序,能通过某种介质,把数据从一边送到另一边。
而为了让这件事别乱套,大家又必须提前约好规则。
这套规则,就是协议。
所以这一整套关系其实是:
- 通信:程序之间交换数据
- 介质:数据通过什么东西传过去
- 协议:双方按什么规则收发数据
你把这三个词分清楚,后面再看 HTTP、WebSocket、TCP、UDP 这些词,就不会一股脑全搅在一起了。
什么是协议?你可以把它当成前后端先对好的"说话规则"
协议这个词,很多人一听就觉得很大、很底层、很唬人。但是其实你只要做过一次前后端联调,就已经在天天碰它了。
前后端联调最经典的场面是什么?
前端说:"我接口都调了,怎么还是报错?"
后端说:"你传的什么玩意,我这边根本收不到。"
然后两边开始互相怀疑人生。
但很多时候,问题根本不是谁代码写错了。

而是大家压根没按同一套规则说话。
比如前端以为接口是:
- 地址:
/api/user/login - 方法:
POST - 参数放在
body - 用户名字段叫
username - 密码字段叫
password
结果后端实际写的是:
- 地址:
/user/login - 方法:
GET - 参数放在 query
- 用户名字段叫
name - 密码字段叫
pwd
结果......
所以协议这玩意,别把它想得太玄。
你在前后端联调时,对接口地址、请求方式、参数位置、字段名字、返回结构,本质上就是在对协议。
也就是说:协议就是通信双方提前约好的说话方式。
比如一个很常见的登录接口,前后端在真正开写之前,往往就得先把这份"说话规则"对清楚:
json
{
"apiName": "用户登录",
"method": "POST",
"url": "/api/user/login",
"headers": {
"Content-Type": "application/json"
},
"requestBody": {
"username": "zhangsan",
"password": "123456"
},
"responseBody": {
"code": 0,
"message": "ok",
"data": {
"userId": 1001,
"nickname": "张三",
"token": "eyJhbGciOi..."
}
}
}
你看,这里面其实已经把"怎么说话"全定完了:
- 你要访问哪个地址
- 你要用什么方式发请求
- 你要带什么请求头
- 你请求体长什么样
- 我返回的数据长什么样
code、message、data分别代表什么
这就是协议。
它不一定非要写成一本厚厚的标准文档。
很多时候,前后端对着一份接口文档、一个 Apifox 页面、一个 Swagger 地址,讨论的其实就是这些东西。
比如后端会说:"我这个接口返回 code = 0 表示成功。"
前端就知道:"哦,那我不能拿 HTTP 状态码直接当业务成功,我还得看 code。"
再比如前端会问:"登录成功以后,token 从哪里取?"
后端说:"放在 data.token 里。"
这时候双方其实还是在对协议。
你会发现,协议根本不神秘。
它就是把下面这些事情提前说清楚:
- 你怎么找我?
- 你怎么把数据给我?
- 我怎么把结果回给你?
- 出错了我们各自怎么看懂?
如果这些东西没约好,就算网是通的,程序也聊不起来。
这就像两个人打电话,线路明明接通了,但一个说中文,一个说摩斯电码,结果还是白搭。
所以后面你看到 HTTP、WebSocket,不要把它们先当成两个要死记硬背的名词。
你可以先把它们理解成:
网络通信里,不同场景下的不同说话规矩。
HTTP 更像是:我问一句,你答一句。
WebSocket 更像是:咱们先把线接通,后面谁有话谁就随时说。
它们都属于协议。
只是适合的场景不一样而已。
先讲最常见的:HTTP 为什么几乎哪都有它
HTTP 你几乎天天在用。
打开网页在用。
前端调接口在用。
移动端 App 拉数据在用。
后端服务之间互相调用,很多时候也在用。
为什么它这么常见?
因为它的思路特别符合大多数业务场景:
我发一个请求。
你给我一个响应。
这一问一答,事情就结束。

比如:
js
fetch('/api/user')
你可以把它脑补成:
客户端:我想要用户信息。
服务器:给你,这是用户信息。
完。
HTTP 这种模式很像去窗口办事:
你递一张表。
对面处理。
然后把结果递回来。
所以 HTTP 最大的特点就是:
请求一次,响应一次。
它很适合:
- 打开页面
- 获取列表
- 提交表单
- 调用接口
- 上传下载
也就是说,只要你的业务是"问一次,答一次",HTTP 通常就够用了。
那 WebSocket 又是干嘛的
如果 HTTP 是"去窗口办一次事",那 WebSocket 更像是:
打通一根一直开着的电话线。
HTTP 的问题在于,它更偏"单次来回"。但有些场景不是这样。
比如:
- 聊天室
- 在线协作
- 实时推送
- 游戏状态同步
- 股票行情 / 监控面板实时更新
这些场景的共同点是:
数据不是你问一次才来一次。 很多时候,对方一有变化,就要立刻推给你。
这时候如果你还用 HTTP,不停地问:
"有新消息吗?" "现在有了吗?" , "现在呢?"
就会显得很笨。
于是 WebSocket 出现了。
它的思路是:
先建立一条长连接。
连接建立好以后,客户端和服务器都可以随时发消息。
这就和 HTTP 很不一样了。
所以你可以这么记:
- HTTP:更像一次性问答
- WebSocket:更像持续保持在线聊天
别把 WebSocket 想得太神秘。
它本质上就是:
让客户端和服务器在一条持续打开的连接上,双向收发消息。
那开发里最常见的几种通信方式,到底该怎么分
你不需要一上来背一堆协议大全。
作为开发,先把最常见的几类通信模式建立起来就够了。
HTTP / HTTPS
最主流的接口通信方式。
特点是请求-响应,一次来回就结束。
绝大多数网页、接口、REST API,都是这个路子。
WebSocket
适合长连接、实时双向通信。
聊天、协同编辑、实时通知、游戏同步这类场景常见。
文件传输 / 下载上传
很多时候本质还是 HTTP,只不过传的不是一小段 JSON,而是文件流、图片、视频这些更大的数据。
数据库连接
严格来说,这也是网络通信。
你的后端程序连 MySQL、Redis、PostgreSQL,本质上也是在和另一台服务程序通过协议说话,只不过它们不是 HTTP,而是数据库自己的协议。
所以一个开发者要建立的正确认知是:
网络通信从来不只是在浏览器里调接口。
只要两个进程不在同一个地方、需要交换数据,那大概率就是网络通信。
最后总结一下......一个开发者该有的网络结构观
写到这里,重点其实不是"背了几个协议名",而是脑子里要开始有一张结构图。
至少要有下面这几个角色:
客户端
主动发请求的程序。
浏览器、手机 App、小程序、桌面客户端,都可以是客户端。
服务器
对外提供能力的程序。
它长期在线,等请求进来,然后处理后返回结果。
网络
负责把数据从一边送到另一边。
这里面会经过很多你暂时不用深究的中间环节,但你至少要知道:数据不是"瞬移",而是在网络里传。
协议
双方通信时共同遵守的规则。
HTTP 是一种。
WebSocket 是一种。
数据库协议也是一种。
回到你最常写的那句代码
我们最后再回到这句很多人天天写、但未必真的理解的话:
js
fetch('/api/user')
它表面上像一句普通代码。
但背后其实是一整条链:
浏览器里的客户端代码,发起一个 HTTP 请求;
请求通过操作系统和网卡送出去;
网络把它送到服务器;
服务器程序处理后返回响应;
响应再经过网络、操作系统、浏览器,最后回到你的 JavaScript;
于是你的 then、await、回调逻辑继续执行。
也就是说,你写下的根本不只是"调了个接口"。
你其实是在启动一次跨机器通信。