古法编程秘籍(七):互联网到底是什么?把两台电脑怎么说话搞懂就够了

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..."
    }
  }
}

你看,这里面其实已经把"怎么说话"全定完了:

  • 你要访问哪个地址
  • 你要用什么方式发请求
  • 你要带什么请求头
  • 你请求体长什么样
  • 我返回的数据长什么样
  • codemessagedata 分别代表什么

这就是协议。

它不一定非要写成一本厚厚的标准文档。

很多时候,前后端对着一份接口文档、一个 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;

于是你的 thenawait、回调逻辑继续执行。

也就是说,你写下的根本不只是"调了个接口"。

你其实是在启动一次跨机器通信。

相关推荐
snow@li1 小时前
SEO-文章标题:写文章时候,分类+主标题+大纲+解释 作为标题 / 不点进去也知道全文覆盖什么 / 标题即架构
前端
Hommy882 小时前
【剪映小助手】添加图片接口(Add Images)
后端·github·剪映小助手·视频剪辑自动化
GetcharZp2 小时前
别再盲目用 OpenCV 读图了,这才是 CV 预处理的终极杀手锏!
后端
kyriewen2 小时前
Git Commit 前自动修复代码风格?配置 Husky + lint-staged,从此 CR 只聊逻辑
前端·git·面试
小和尚同志2 小时前
AI 自动化测试探索(一):Playwright MCP
前端·人工智能·aigc
老马识途2.03 小时前
在AI的帮助下理解spring的启动过程
java·前端·spring
徐小夕3 小时前
Loop Engineering 深度解析与实战指南(全网最全)
前端·算法·github
袁小皮皮不皮3 小时前
3.HCIP OSPF补充知识(优化版)
服务器·网络·数据库·网络协议·智能路由器
运筹vivo@4 小时前
Python ContextVar 底层机制与内存模型拆解
前端·数据库·python