2. 传输层核心职责:端到端通信、端口号与套接字、多路复用与多路分解
好,我们正式开始传输层。很多初学者会把传输层和网络层搞混,我当年也踩过这个坑。简单说,网络层负责把数据包从一台机器送到另一台机器,而传输层负责把数据交给这台机器上的哪个程序。这就是端到端通信的本质。
2.1 端到端通信:从主机到进程
网络层提供的是主机到主机的通信。你的数据包到了目标机器的网卡,然后呢?谁来处理它?是浏览器?还是游戏客户端?还是邮件客户端?
传输层就是干这个的。它把通信的粒度从「主机」细化到了「进程」。我习惯把网络层比作快递把包裹送到你家小区门口,传输层则是快递员按门牌号把包裹送到你手上。没有传输层,数据到了机器就没人管了。
核心要点:传输层提供的是进程间的逻辑通信,而不是主机间的物理通信。这是理解 TCP 和 UDP 一切差异的基础。
2.2 端口号:进程的身份证
端口号是个 16 位的整数,范围 0-65535。它用来标识一台机器上的不同进程。说白了,端口号就是进程在网络上暴露的「门牌号」。
端口号分三类:
| 类型 | 范围 | 说明 | 常见例子 |
|---|---|---|---|
| 知名端口 | 0-1023 | 由 IANA 分配,固定给系统服务 | HTTP(80)、HTTPS(443)、FTP(21) |
| 注册端口 | 1024-49151 | 可由用户进程申请,但建议避免冲突 | MySQL(3306)、Redis(6379) |
| 动态/私有端口 | 49152-65535 | 客户端临时使用,系统自动分配 | 浏览器发起连接时用的临时端口 |
我的经验:我曾经在调试一个 Web 服务时,怎么都连不上。查了半天,发现是另一个服务占用了 8080 端口。用 netstat -ano 一看就明白了。所以排查网络问题时,先看看端口有没有被占用,这是基本功。
2.3 套接字:通信的端点
套接字(Socket)是传输层提供给应用层的编程接口。一个套接字由 IP 地址 + 端口号 唯一确定。
举个例子:
// 一个 TCP 套接字的创建(伪代码)
socket_fd = socket(AF_INET, SOCK_STREAM, 0);
bind(socket_fd, (struct sockaddr*)&addr, sizeof(addr));
listen(socket_fd, 5);
// 此时套接字绑定了 192.168.1.10:8080
套接字是传输层与上层应用之间的桥梁。应用层通过套接字发送数据,传输层负责把数据封装成报文段,然后交给网络层。
2.4 多路复用与多路分解
这两个概念是传输层的核心机制。我建议你把它理解成一个快递分拣中心。
多路复用(Multiplexing)
发送端有多个应用进程(比如浏览器、、邮件客户端)都要发送数据。传输层从多个套接字收集数据,为每个数据块加上头部信息(包含源端口和目的端口),然后交给网络层统一发送。这个过程就是多路复用。
说白了,就是把多个来源的数据合并到一条传输通道上。
多路分解(Demultiplexing)
接收端收到一个数据包后,传输层查看报文段头部的目的端口号,然后把这个数据交给对应的套接字,再由套接字交给对应的应用进程。这就是多路分解。
你想想看,你的电脑同时开着浏览器和游戏,网络层收到一个数据包,传输层怎么知道该给谁?就是靠端口号来分解的。
注意:UDP 和 TCP 的多路分解方式不同。UDP 只根据目的端口号来分解,而 TCP 需要根据源 IP、源端口、目的 IP、目的端口四个要素来分解。这也是为什么 TCP 的连接是「一对一」的,而 UDP 可以「一对多」。
下面这张图展示了多路复用与多路分解的完整流程:
2.5 无连接 vs 面向连接的多路分解
UDP 和 TCP 在多路分解上的实现完全不同,这也是它们最根本的区别之一。
UDP 的多路分解
UDP 是无连接的。它只根据目的端口号来分解数据报。也就是说,只要目的端口号匹配,不管数据从哪来,都交给同一个套接字。
这意味着:
- 一个 UDP 套接字可以和多个客户端通信
- 服务器只需要一个套接字就能服务所有客户端
- 没有连接建立和拆除的开销
TCP 的多路分解
TCP 是面向连接的。它需要根据四个要素来分解:源 IP、源端口、目的 IP、目的端口。这四个要素唯一确定一个 TCP 连接。
这意味着:
- 每个 TCP 连接对应一个唯一的四元组
- 服务器需要为每个客户端创建一个新的套接字
- 连接是点对点的,一个 TCP 连接只能服务于两个进程
关键区别:UDP 的套接字是「一对多」的,一个套接字可以接收来自多个源的数据。TCP 的套接字是「一对一」的,一个套接字只对应一个连接。这也是为什么 TCP 能提供可靠的、有序的数据传输,而 UDP 不能。
2.6 实际项目中的选择
我在做即时通讯项目时,就面临过这个选择。消息推送需要低延迟,但可以容忍偶尔丢包。我选择了 UDP,配合应用层的重传机制。为什么?因为 TCP 的拥塞控制会导致延迟抖动,用户体验反而更差。
但如果是文件传输或网页浏览,我肯定选 TCP。丢一个包整个页面就乱了,这种场景下可靠性比速度重要得多。
嗯,这里要注意:没有绝对的好坏,只有适合不适合。理解多路复用和多路分解的原理,你就能明白为什么 TCP 和 UDP 会有这些差异。