CS144Lab/writeups/lab3.md
2023-02-17 06:40:58 +00:00

4.6 KiB
Raw Blame History

Lab 3 Writeup

My name: Catfood

My SUNet ID: 998244353

I collaborated with: An orange cat

This lab took me about 4.5 hours to do.

Program Structure and Design of the TCPSender:

Sender 需要关注两个部分:生成并发送 Segment接收 ACK 并对状态做出相应的调整。这对应了框架中给出的fill_windowack_received这两个函数。除此之外,还需要另外一个计时器来控制超时。

超时逻辑

按照讲义的顺序,先来理解一下超时的逻辑。比较的反直觉。

  • 首先明确,在实际的实现中,我们的计时器和理论课上讲的 Go-Back-N 或者是 SR 中的理想化的超时逻辑不一样,这里的计时器只有一个,通过 ACK 来控制回收,而不是每个 packet 或者每个 byte 有一个计时器(虽然或许可以这么做,但是比较消耗资源,写起来也很复杂)。
  • 涉及计时器的几个状态:
    1. 发送一个有长度的 Segment无条件 start 计时器。
    2. 收到一个ACK如果这个 ACK 重复上一次或者更久之前的 ACK那么不管他如果这个 ACK 表示新的 Segment 被收到
      1. 将计时器 reset 为全局初始值,重传计数清空
      2. 如果还有 outstanding datastart 计数器
      3. 如果没有 outstanding datastop 计数器(应该是不需要 reset 的) 不过这里有一个问题:如果这个 ACK 是半个 packet 会咋样?讲义上没有明确,但是按照其他地方的描述,可以当作不是新的 ACK 来处理。
    3. 时钟 tick首先判断是否启动然后减少计时器再判断是否超时如果超时
      1. 重传 outstanding 队列里面的第一个 segment。
      2. 如果此时的 window 不为0增加重传计数加倍初始值
      3. 局部 reset 计数器
      4. start 计数器

这样就可以分析出计时器需要的一些接口和状态:

  • 状态: is_start 计时器启动状态 cr_count 重传计数 ti_count 超时计数 RTO 局部初始值
  • 接口: start() stop() 启动停止计时器 reset() reset_init(size_t RTO) 重置计时器(仅重置 ti_countis_start| 全部重置 retransmit() 增加重传计数并加倍RTO tick(size_t ms) 减少计时器最少减少到0单位就是外面 tick 的单位
  • 包装器: is_started() consecutive_retransmissions() expired()

发送逻辑

最基本的发送操作,就是把一个 TCPSegment 塞进 _segment_out 这个队列里面,然后由外部的调用者来负责交给下层网络。

fill_window 是最主要的发送函数,他的功能可以描述为:尽可能地发送数据,除非字节流空或者 window 不允许。此外,还需要维护 SYN、FIN 以及分段。

主要的问题在于分段,因为标志位占用空间。

先不考虑分段,那么发送过程应该是这样的: 0. 首先计算可发送数据大小,也就是 window_size - (_next_seqno - _unack_seqno)

  1. 准备需要发送的数据
    1. 首先判断是否需要发 SYN (判断 _next_seqno是不是0
    2. 然后从字节流读取所有的可发送数据。(即使读出来的比要求的要少也无所谓。)
    3. 再判断是否需要发 FIN 判断EOF、窗口允许、是否已经发过_next_seqno < bytes_read + 2
  2. 组装数据为 TCPSegment
  3. 如果组装出来的队列在序列空间内有长度,那么塞进发送队列里,增加_next_seqno;同时单独维护内部的 outstanding 队列(不能用发送队列,因为爱会消失)并启动计时器。否则就不管。

然后考虑分段的问题: 这里用一个循环,每次取原始 payload 的一个 MAX_PAYLOAD_SIZE 长度的 substr,然后用指针记一下取到哪里了,这里已经不需要考虑窗口了,因为数据准备阶段都已经计算好了。这时候只需要判断是否为第一个和最后一个来附加可能的 SYN 和 FIN 就行了。

Implementation Challenges:

把思路理清楚就好了,这个里面的 corner case 不是很离谱,主要的 bug 还是无符号计算溢出,减法一定要注意啊。 最离谱的反而是如何组装一个 TCPSegment一开始用 parse 方法,结果发现因为 header 不全直接失败。然后才发现它的包装器返回的是一个,引用?!!直接赋值即可。

Remaining Bugs: []

  • Optional: I had unexpected difficulty with: [describe]

  • Optional: I think you could make this lab better by: [describe]

  • Optional: I was surprised by: [describe]

  • Optional: I'm not sure about: [这,说实在我都不知道我的代码是怎么通过测试的]