计算机连接起来以后,数据到底应该送到哪里,又应该怎样理解收到的数据?
围绕这里,就需要我们先了解四个概念:
- IP 地址
- 端口号
- 协议
- 五元组
它们之间有联系,我们把它们放到同一个场景里理解。
1. 网络通信基础
我们平常会说,我的电脑正在和服务器通信,但再往里面看,真正需要交换数据的是:两边正在运行的程序,也就是进程。
就比如:
| 场景 | 发起通信的程序 | 接收通信的程序 |
|---|---|---|
| 浏览网页 | 浏览器 | 网站服务器程序 |
| 使用 JDBC 查询数据库 | Java 程序 | MySQL 服务程序 |
| 聊天 | 聊天客户端 | 聊天服务器程序 |
之前学习 JDBC 的时候就写过相关代码:
java
DriverManager.getConnection(
"jdbc:mysql://127.0.0.1:3306/java113",
user,
password
);
此处正是设计网络通信了,Java 程序通过 JDBC 驱动,与 MySQL 服务程序建立连接、交换数据。所以会说,网络通信更具体的说,是进程之间基于网络传输数据。
至于接收数据的程序在哪台设备上 以及找到设备以后把数据交给哪个程序这就需要 IP 和端口号了。
1.1 IP 地址
IP 地址可以理解为,用于定位主机的网络地址
就像是寄快递填写的,某个城市、某条街道、某栋楼
同样,发送网络数据时,也需要有目标地址,类似192.168.1.20
更严谨地说,IP 地址配置在网络接口上,一台设备可以有多个 IP 地址,并不是每台电脑永远只有一个地址。
此处我们讨论的是 IPv4:
IPv4 地址占 32 位,也就是 4 个字节,是一个 32 位的二进制数,通常分成四个 8 位部分。
使用的是点分十进制表示法
| 部分 | 占用位数 | 十进制范围 |
|---|---|---|
| 第一部分 | 8 位 | 0~255 |
| 第二部分 | 8 位 | 0~255 |
| 第三部分 | 8 位 | 0~255 |
| 第四部分 | 8 位 | 0~255 |
而常用的 127.0.0.1,则是一个回环地址,通常用于访问本地的网络服务~
而 localhost,则是主机名,但常被解析为127.0.0.1,当然也有可能解析为 IPv6 的 ::1
| 项目 | localhost |
127.0.0.1 |
|---|---|---|
| 类型 | 主机名 | IPv4 地址 |
| 含义 | 本地主机 | IPv4 回环地址 |
| 是否需要解析 | 需要 | 不需要 |
| 可能解析为 | 127.0.0.1 或 ::1 |
永远是 IPv4 |
| 网络协议 | 可能 IPv4 / IPv6 | IPv4 |
对于当前很多 AI 小白利用 AI 做项目的时候,就常有将访问本地资源的localhost分享给他人而搞出段子~
你可以这样记住区别:
localhost是主机名,127.0.0.1是 IPv4 回环地址。localhost通常会解析到127.0.0.1,但也可能解析到 IPv6 的::1,所以两者不能认为绝对完全相同。
1.2 端口号
端口号就是负责,数据发送到目标主机时,将数据送到哪一个进程的。
用快递类比就是:
- IP 地址类似收货地址。
- 端口号类似这个地址下的收件入口。
通常,一台设备可以有:
| 服务 | 假设监听的端口 |
|---|---|
| MySQL 服务 | 3306 |
| Java 服务 A | 8080 |
| Java 服务 B | 9090 |
那么192.168.1.20:3306和192.168.1.20:8080,虽然目标设备相同,但目标服务入口不同。
而端口号的范围也就是 TCP、UDP 等传输协议使用 16 位端口号,所以数值范围是 0~65535。因为 216=65536
而端口号和进程编号 PID 不是一回事~
PID 只是用于操作系统用来标识进程的,而一个进程可以拥有多个端口号~
实际接收数据时,操作系统会根据地址、传输协议、端口等信息,找到对应的套接字,再交给应用程序处理。后面学习 Java Socket 时,会具体使用它。
1.3 协议
协议的作用就相当于双方提前约定"数据怎样组织、怎样解释,以及通信过程中怎样行动"
就比如会这样约定:
| 约定 | 目的 |
|---|---|
| 文字使用 UTF-8 编码 | 接收方知道怎样解码 |
| 消息中携带长度信息 | 接收方知道正文有多少字节 |
| 某种消息表示请求,另一种表示响应 | 接收方知道该怎样处理 |
这里是在举自定义协议规则的例子,具体协议会有自己的设计。
所以,协议不仅规定数据格式,也会规定通信行为。
TCP、UDP、HTTP、MySQL 协议等,都在不同层次解决通信中的问题。
你写 JDBC 时,不需要自己把 SQL 拼成 MySQL 协议的数据包,是因为:
JDBC 驱动已经帮你实现了与 MySQL 服务通信的协议处理。
1.4 五元组
对于常见的 TCP、UDP 通信,这五项是:
| 字段 | 含义 |
|---|---|
| 源 IP 地址 | 数据从哪个网络地址发出 |
| 源端口号 | 发送方使用的端口 |
| 目的 IP 地址 | 数据发往哪个网络地址 |
| 目的端口号 | 接收方使用的端口 |
| 传输层协议 | 使用 TCP、UDP 等哪种协议 |
此处的"源"和"目的",是相对于当前的数据传输方向来说的~

我这里继续拿 Java 访问 MySQL 举例:
假设
- Java 程序所在设备的 IP 是
192.168.1.10- MySQL 所在设备的 IP 是
192.168.1.20- Java 这次连接使用本地端口
50000- MySQL 服务监听
3306- 使用 TCP 通信
Java 向 MySQL 发送数据时:
| 五元组字段 | 值 |
|---|---|
| 源 IP | 192.168.1.10 |
| 源端口 | 50000 |
| 目的 IP | 192.168.1.20 |
| 目的端口 | 3306 |
| 协议 | TCP |
MySQL 回复时,源和目的会交换:
| 五元组字段 | 值 |
|---|---|
| 源 IP | 192.168.1.20 |
| 源端口 | 3306 |
| 目的 IP | 192.168.1.10 |
| 目的端口 | 50000 |
| 协议 | TCP |
所以显然,客户端并不永远是"源",服务器也不永远是"目的"。谁正在发送,谁就是这个方向上的源。
假设很多客户端都在连接同一个 MySQL 服务。
它们的目的地址和目的端口可能都是192.168.1.20:3306
服务器还需要区分:
这份数据来自哪一条连接?回复应该发给谁?
这就需要结合客户端的地址和端口。
因此,同一个服务端口可以接待很多连接,不是"一条连接就独占整个服务端口"。
在 Windows 中,可以通过:
bash
netstat -ano
三个选项分别表示:
| 选项 | 作用 |
|---|---|
-a |
显示活动连接及监听端口等信息 |
-n |
使用数字显示地址和端口 |
-o |
显示相关连接的进程编号 PID |

如果需要过滤(一般是通过端口号或进程 PID 过滤),可以使用 netstat -ano | findstr 过滤字符串
2. 协议分层
当前的问题是,刚刚上面的所有工作该如何分工。
解决方法是,把网络通信的工作划分为不同层次,各层负责自己的任务,并通过约定好的接口协作。
2.1 同层之间遵守协议,相邻层之间使用服务

先来看上图中的例子:
把打电话分成两层:
语言层 负责双方用什么语言交流,怎样理解对方说的话
通信设备层 负责怎样通过设备传递声音
这里其实有两套规则:
语言层规则
双方需要理解汉语,才能明白:
"你明天几点到?"
通信设备层规则
电话系统需要按照相应规则,把声音传递给对方。
它负责让对方听到声音。
其实也就是,第一层封装,第二层解耦
- 上层协议不需要了解下层协议的细节~打电话的人会说话就行,不需要理解电话机原理
- 分层后,灵活的替换其中的某一层,对于整体的工作过程影响很小
关于下方的两个改变,主要改变的是:上层怎样组织和理解表达内容
底下的电话系统仍然可传递声音。
所以分层的价值就是,在接口和服务约定保持兼容的情况下,一层的变化可以尽量减少对其他层的影响。
当然,实际网络通信比这个例子要复杂,一层的变化可以尽量减少对其他层的影响。
其实分层是比较类似"面向接口编程"
就类似调用方使用接口,提供方通过接口提供服务。
网络分层也有类似的思想:
- 上层使用下层提供的服务
- 下层通过接口提供能力,隐藏自己的实现细节
同层之间遵守协议,相邻层之间使用服务
此处要分清两个方向:
同一设备内部,上层通过接口,使用下层的服务
通信双方之间,双方对应层需要遵守相应的协议
2.2 OSI 七层模型
OSI(Open System Interconnection)开放系统互联
但 OSI 七层网络模型复杂也不实用,所以 OSI 七层模型都没有落地实现
实际组建网络时,只是以 OSI 七层模型设计中的部分分层,也就是以下 TCP/IP 五层(或四层)模型来实现

2.3 TCP/IP 五层(或四层)模型
五层或四层只是视角不同罢了~
TCP/IP 是一组协议的代名词,其中包含 TCP、UDP、IP 以及许多应用层协议

- 首先是应用层。它关注的是应用程序之间,应该交换什么消息,又该如何理解这些信息。就比如 HTTP(网页等资源的请求与响应)、SMTP(电子邮件传输)、FTP(文件传输)等。在编写 Java 网络程序,主要就是在这一层组织业务消息,并通过 Socket 等接口使用下层的传输服务。当然,这一层你也可以理解为买家在拿到包裹后如何使用~
- 传输层关注的是 通信两端的应用怎样交换数据(如 TCP 传输控制协议)。 之前了解到的端口号就在此处发挥作用。字面意思就是,两个任意设备之间的通信,不考虑中间过程,只考虑起点和终点。就像是卖家在寄件时,只负责填发货地址和到货地址即可。
- 网络层,处理地址与跨网络转发,也就是负责地址管理和路由选择。两台主机之间可能隔着很多网络和路由器(就像是《无敌破坏王》中那样到网络中心一样),这一层就关心数据下一步该往哪里转发,对应的就是前面的 IP 地址。路由器会根据目的 IP 地址和路由表等信息,选择下一跳。并不要求发送方提前固定好整条完整路径。
- 数据链路层,则是负责设备之间的数据帧的传送和识别。这一层通常会处理数据帧的组织、链路上的寻址(如以太网中的 MAC 地址)、介质访问、差错检测等。主要就是关注当前链路上怎样把数据送到下一站。
- 物理层,真正传递信号。数据帧最终还需要通过实际介质传出去。而物理层就负责光/电信号的传递方式。这一层设计信号编号、传输介质、接口等问题。所以应用层所表达的业务消息,最终需要借助这些物理能力完成传递。
就相当于,我在淘宝上买了床刷子
我则关心床刷子买到之后,我要如何使用 ->应用层
卖家只要关心收件人信息 ->传输层
物流公司则关心包裹以怎样路径传输的 ->网络层
快递小哥/货车司机考虑的是相邻节点 ->数据链路层
国家在铺桥修路 ->物理层
写代码主要就是工作在应用层了~
而像主机、路由器、交换机就分别对应下方:
主机 -> 工作过程涉及到 从物理层->应用层(通过应用程序满足网络通信的需求)
路由器-> 工作过程涉及到 从物理层->网络层(组建局域网,进行网络)
交换机-> 工作过程涉及到 从物理层->数据链路层(对路由器的接口的扩展,不需要考虑组网的问题)
此处说的是传统意义上的交换机和路由器,也称为二层交换机(工作在 TCP/IP 五层模型的下两层)、三层路由器(工作在 TCP/IP 五层模型的下三层)
上面的工作区间也是一个经典的面试题
2.4 一则信息的全过程
2.4.1 数据发送
假设我通过 QQ,发送 Hello 给好友:
- 应用程序会先获取到用户输入,构造一个应用层的数据包。这个应用层数据包就会遵循应用层的协议(这部分往往是开发者自己定义的)。假设此处的协议格式是:
发送者的 qq 号,接收者的 qq,消息的时间,消息正文。(类似下图这种)
就以此为例:1234567,7654321,2026-10-04,12:00:00,Hello(当然实际是会加密的)

注:上述所说的数据包往往是"结构化数据"。发送数据的时候,把结构化数据 -> 字符串/二进制 bit 流(序列化);接收数据的时候,字符串/bit 流 -> 结构化数据(反序列化)

- 应用程序调用传输层提供的接口(API 也就是 socket api),把数据交给传输层。传输层拿到数据之后,构造出"传输层数据包"。传输层的协议主要就是两个:TCP、UDP。得到数据包形式:TCP 数据包 = TCP 报头(header) + TCP 载荷(payload)。TCP 载荷就是应用层数据包。

- 传输层构造好数据之后,继续调用网络层的 api,把传输层的数据包交给网络层。网络层继续进行处理。网络层最主要的协议就是 IP 协议。IP 协议会继续对上述数据包进行加工 -> 拼上 IP 报头~。得到的 IP 数据包形式就是: IP 数据包 = IP 报头 + IP 载荷(整个传输层的数据包)。其中 IP 报头包含的信息也有很多的,就比如源 IP 或 目的 IP。
注:IP 报头还会记录传输层使用的是哪个协议。同样的,传输层报头中也会记录应用层使用的是哪个协议(准确的说是数据交给哪个应用程序)...

- IP 协议继续调用数据链路层的 api ,把 IP 数据包交给数据链路层。数据链路层中的核心协议就是"以太网"这个协议。以太网也会在网络层数据包的基础上进一步加工~
到目前为止,数据还在当前主机上转悠,还没出门~

- 以太网继续把这样的数据交给硬件设备(网卡)。网卡会按上述二进制数据最终以光信号/电信号/电磁波信号 传播出去此时数据才算出门了

封装顺序就是 应用数据 -> TCP 报文段 -> IP 数据包 -> 以太网帧 -> 信号
从上到下,数据都要进一步加工
解释一下:网络数据包/帧/报/段
段(segment) TCP 用段
报(Datagram) UDP用报
包(packet) IP 用包
帧(frame) 数据链路层 以太网数据帧
但其实日常工作中,确实也不会明确区分这些概念~
2.4.2 数据接收
刚才是数据发送所进行的全过程,接下来是数据接收回来一步一步解封装
- 数据到达接收方的网卡。网卡就会把光信号还原成二进制,随后把二进制数据交给上层数据链路层。
补充一下:不同的网络连接方式,数据链路层使用的协议和帧格式也可能不同
网线/光线来的数据, 数据链路层 -> 以太网
WiFi 来的数据,数据链路层 -> IEEE 802.11 协议

- 数据链路层按照以太网协议进行解析。把报头和报尾取出来,剩下的载荷往上传递给网络层。以太网的帧头中就有专门的属性,描述了网络层使用哪个协议

- 网络层拿到这个数据之后,按照 IP 协议的格式解析:再把载荷数据交给传输层。IP 报头中也有专门的属性,描述了传输层使用哪个协议

- 传输层拿到数据之后,也是类似,按照 TCP 协议来解析。取出载荷,交给应用层。传输层的报头中,通过目的端口号,告知我们数据交给哪个应用程序

QQ 应用程序解析应用层数据,拿到关键信息,展示到界面上,给出提示~
不同的应用程序,自然使用不同的应用层协议来解析~

传输的中间过程中,也是涉及到封装分用的。
交换机 只需要封装分用到数据链路层即可
交换机是工作在数据链路层(二层转发)
主机的数据 -> 交换机。交换机收到之后,物理层解析,然后数据链路层解析(没有网络层)~ 重新构造出新的以太网数据帧发送给下一个设备。
数据链路层中,得到以太网数据帧的帧头,信息就足以支持交换机进行下一步工作
路由器是工作在网络层(三层转发)
主机的数据 -> 路由器。物理层,数据链路层,网络层解析(没有传输层),重新构造出新的网络数据包。构造出以太网数据帧,构造出二进制数据,再进行转发
感谢你读到这里,这也是我学习路上的一个小小记录。希望以后回头看时,能看到自己的成长
可以关注我的博客Syrena-Blog