计算机网络 - 传输层

注意
本文最后更新于 2022-06-14,文中内容可能已过时。

寄!

自己会了的就不写了

端口号

  • FTP: 20 数据, 21 控制
  • Telnet: 23
  • SMTP: 25 (就发邮件的那个)
  • DNS: 53
  • TFTP: 69
  • HTTP: 80
  • SNMP: 161
  • RPC: 111

(感觉最多就选择题, 不知道就不知道吧)

Socket

UDP

不可靠

源端口号 2B, 目的端口号 2B, 总长度 2B, 校验和 2B

可选 (反正都是不可靠), 不校验的话填 FFFF.

校验和除了整个包的, 还要加上源 IP, 目的 IP, UDP 长度, UDP 协议 (卡在了中间位置, 为简单叙述, 后面补0) (17, 0x00110000)

啊对, UDP 长度加了两次.

TCP

可靠, 提供全双工服务 (面向进程, 进程认为是全双工, 物理层的介质是不是不重要)

TCP 面向进程时, 以 字节流 的形式收发数据. 但向下传递, 不可能以一个字节当成一个数据包然后封 IP 头.

发送和接收都有缓冲区. 面向网络层时, TCP 从缓存区取几个字节组合在一起, 称为 Segment, 然后封 TCP 头, 传给网络层.

可靠, 且面向字节 (虽然下一层的实现是以段为单位的), 所以需要为字节标号, 称为序列号. 编号不从 0 开始, 而是随机一个数. 全双工服务, 两边都有编号. ACK 可以捎带在数据中发送.

ACK 带下一个预期收到的序列号, 由于是按段收发的, 所以 ACK 发的预期序列号不连续, 而是每一段的第一个字节的序列号.

单独 ACK 不编号, SYN 编号 1 个字节.

三次握手

发送方 A, 序列号 8000. 接收方 B, 序列号 15000.

$$\ce{A ->[SYN][8000] B}$$ $$\ce{A <-[SYN ACK 8001][15000] B}$$ $$\ce{A ->[ACK\ 15001][8000] B}$$

B 发的 ACK 确认, 同时也发 SYN 建立双向的链接. A 的确认不带数据, 则不占序列号, 即和之前的序列号一样. 若带数据, 则序列号就是数据段的序列号.

四次挥手

发送 FIN 主动关闭自己到对方的单向通道, 需要对方确认.

假设 A 的序列号到了 10000, B 的到了 20000.

$$\ce{A ->[FIN ACK 20001][10000] B}$$ $$\ce{A <-[ACK 10001][20000] B}$$

此时 A 不能向 B 发数据了, 因为 A 关闭了, 但是 B 还可以向 A 发数据, 并且 A 发送 ACK 确认. 假设 B 发完后, 序列号到了 21000.

$$\ce{A <-[FIN ACK 10001][21000] B}$$ $$\ce{A ->[ACK\ 21001][10000] B}$$

这时 B 关闭了单向通道并由 A 确认了.

如果 A 单向向 B 发送数据, B 并没有要发送数据, 那么可以把 B 向 A 发的 ACK 中加入 FIN.

TCP 有发送窗口, 像 ARQ 那样. 窗口的大小是可变的 (序列号不取模, 不会出现问题).

接收方有缓存区, 收到数据后, 接收方算一下自己还能收多少, 称为接收方窗口, 记为 $rwnd$, 然后发 ACK 的时候告诉发送方, 我还能收 $rwnd$ 个字节, 然后发送方就会调整自己的窗口为 $rwnd$ (非常不严谨, 先这么想着).

把接收方的缓存区看成 选择重发 ARQ 的接收窗口. 和选择重发 ARQ 很像. 区别在于:

  1. 不发 NAK, 而是 ACK (因为 TCP 就没设计 NAK)
  2. 没有一次个序列号只发一个 NAK (TCP 里是 ACK) 的要求
  3. 如果发送方接收到了三个相同序列号的 ACK, 则会重发, 而不是接收到 NAK 就立即重发 (这个叫快速重传)
  4. 每个个段的 重传超时Retransmission Time Out (RTO) 定时器是根据 往返时间Round Trip Time (RTT) 动态更新的

这里由缓冲区, 段可能是失序到达的. TCP 面向进程时, 字节是有序的, 所以一定要等一个前缀到了, 才能向上传给进程.

除了接收方告知发送方自己还能接收多少外, 发送方也可以根据情况, 判断整个网络的状况, 决定窗口大小, 即一次发送段的次数.

这里, 发送方维护一个拥塞窗口大小, 记为 $cwnd$. 基本的逻辑就是, 判断网络状况如果不好的话, 减小 $cwnd$. 然后自己发的少了, 网络应该会更好一点, 于是每次发送就多发一点 (增大 $cwnd$), 直到最大 (为方便, 记为 $M$). 这个阈值也需要根据自己判断的网络状况动态维护.

然后实际上的窗口大小是 $\min \{rwnd, cwnd\}$ (所以说前面的不严谨)

当需要重发时, 即计时器到达, 或者接受到了三个 ACK, 发送方就认为网络可能发生了拥塞. 窗口一定小于接收方还能收的, 所以不会是自己发的快了导致接收方收了而丢弃. 那么问题只可能出现在了网络拥塞上.

如果计时器到达, 那么说明自己的包很可能被堵在路上. 于是判断网络堵塞很严重. 此时采取以下措施:

  1. $W \leftarrow \frac{cwnd}2$.
  2. $cwnd \leftarrow 1 MSS$ (最大段长度, 建立链接的时候确定的, 在传输的过程中不变).
  3. 慢速启动: 每完成一次传输 ( 一次传输指的是, 不考虑 ACK, 发送窗口里所有字节[当然要成段]), 计数收到了多少个 ACK (记为 $n$, 一段对应了一个 ACK), 然后 $cwnd \leftarrow cwnd + n\ MSS$. (如果 ACK 不丢失, 那么一次传输后, $cwnd$ 以指数形式增长, 为 $2^k MSS$), 直到达到阈值 ($W$), 即实际上 $cwnd \leftarrow \min \{cwnd + n, W\}$. 到达 $W$ 后, 启动拥堵避免, 即每完成一次传输, $cwnd \leftarrow \min \{cwnd + 1\ MSS, M\}$.

如果收到了三个相同的 ACK, 那么说明自己的包被都在路上, 可是对方的包我能收到, 这说明了网络可能没那么拥堵, 如果我再乱发, 可能就会拥堵了. 此时采取以下措施:

  1. $W \leftarrow \frac{cwnd}2$.
  2. $cwnd \leftarrow W$.
  3. 拥堵避免: 每完成一次传输, $cwnd \leftarrow \min \{cwnd + 1\ MSS, M\}$.

注意阈值是慢启动到拥塞避免的阈值, 所以拥塞避免时, $cwnd \ge W$.

(其实主要是头)

( h 4 e B a 4 d p b e i l r t e n 1 s ) o c u h r e c r c e e k v 6 p e b s o r i u r s t m t e 4 4 B B U R G A C K P S H A C R S T s K e S Y N q s e F I N q 4 B 4 B d e s U t R G p r o w 2 p r n B o t d i n 4 t B 4 B

解释一下部分

  • 头长度: 单位是 4B, 一般头是 20B, 所以这里是 0x5
  • URG: 紧急标志. 一般来说发送方在成段的时候, 是数据是按顺序一字节一字节封装的, 但有时候可能需要不按顺序, 比如发送终止字符, 表示发送终止, 而且这个发送不作数, 所以前面不需要收, 避免无用功. 这时 URG 位置 1, 并将这个紧急字符放在段开始, 同时 URG 指针的值为紧急字符原先在这段数据中的偏移.
  • PSH: 急迫标志, 接收方可能收到很多个数据包, 并且按照先来先服务原则一个个处理. 如果有急迫标志, 那么马上处理.
  • 校验和: 必须使用, 计算方法与 UPD 一样, 也有伪头部, 只不过协议的部分是 6 (0x00060000).