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

85 lines
4.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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_window`和`ack_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 来处理。
4. 时钟 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_count``is_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: [说实在我都不知道我的代码是怎么通过测试的]