Network03 Transport
2022-10-16 16:39:21
Intro
传输层向上给应用层提供进程间通信的服务,向下利用网络层提供的主机到主机的服务。
可以认为传输层本质是对网络层的复用(MUX - DEMUX),因为实质上所有进程共享向外的网络通路,传输层负责把混在一起的网络层数据拆分整理成发到各个进程的数据(通过端口来区分)。当然实际上的做法更加聪明一些,通过 socket 就可以把进程和数据传输的目的地解耦,这样 OS 只需要通过端口号识别出 socket,然后进程主动和特定的 socket 建立连接即可。
通常把传输层的数据叫做 segment。
Socket && Port
端口号用于区分主机上的不同进程,是16bit的无符号整数。著名端口号一般在0~1023中取。
无论是 TCP 还是 UDP,segment 中都包含了 srcPort
和
dstPort
。这是方便了接收方回传消息。
- 接收方的 UDP_Socket 和
(dstIP, dstPort)
一一对应,这意味着两个来自不同主机/进程的 UDP 数据在给到同一个接收方的同一个端口号时,会被分发给同一个 UDP_Socket。 - 而 TCP_Socket 和
(dstIP, dstPort, srcIP, srcPort)
一一对应(面向连接),这意味着两个来自不同主机/进程的 TCP 数据必然会被分发给不同的 socket。
注意这里讲的是已经建立连接之后的情况。考虑连接的建立则是
- 发送方发送特殊的 TCP 数据,用一个 bit 标志表示希望建立连接,同时数据内有目标端口。
- 接收方的 OS 发现这是一个连接建立请求,因此拿着这个端口去寻找监听该端口且正在等待连接建立的进程(可能有多个)
- 建立一个新 socket,将四元组与这个 socket 建立对应。
UDP
- 不可靠,UDP 只是对 IP 的简单封装(包上端口号和checksum)
- 短(header)小(封装)快(没有拥塞控制)
- 无连接,对主机的资源消耗少
- 进程到进程(只有目标地址+端口)
虽然 UDP 看起来很挫,但是 UDP 本身封装得很少,实现简单,没什么包袱,基于 UDP 可以在应用层实现一些可靠的协议(同时比单纯的 TCP 高效),例如 QUIC。
Header
- srcPort
- dstPort
- length
- checksum
TCP
- 可靠
- 拥塞控制
- 面向连接
- 进程到进程(源地址+端口,目标地址+端口)
Re-implement TCP
这一段我还蛮喜欢的。下面谈到的适用于网络中的任何层
trivial
首先假设下层服务是可靠的,那么收发双方只需要直接调用下层服务即可,这样就能搭出一个可靠的服务。
bit flip/lost
假设下层服务只会出现数据错误(但仍然保序),这时引入 retransmission based ARQ protocols(Automatic Repeat reQuest),通常需要有
- 错误检查,即需要能发现 bit flip。这里假设这样的办法总是存在的
- 接收方反馈,即接收后检查确认无误需要告知发送方。一般叫做 ACK(成功) NAK(失败)。
- 重传策略,通常是超时后重传或得知 NAK 后重传。
- 编号,防止接收方无法区分此次消息是重传还是正常发。例如发 m1,收m1发ACK,ACK丢重传 m1,这时接收方就不知道自己的 ACK 到底届到没,也就无从得知这时上一次的重传还是新消息。
注意到一个简单的策略是遇到错误就丢包,因此丢包和错位没有太大区别。
同时为了描述协议,通常用 DFA 的形式来规定状态和操作。
这里的协议状态也很简单,只需要在发送后等待 ACK,在获得 NAK 后重传并重新等待即可。