CS144Lab/writeups/lab4.md
2023-02-18 05:38:32 +00:00

64 lines
5.7 KiB
Markdown
Raw Permalink 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 4 Writeup
=============
My name: Catfood
My SUNet ID: 998244353
I collaborated with: An orange cat
This lab took me about `7` hours to do.
Program Structure and Design of the TCPConnection:
基本操作:封装了一些常用的操作,方便在实际逻辑中调用。
- `_send_all`,把所有 Sender 推进发送队列里面的分段发出去。
- `_send_rst`,丢弃所有未发送的 segment推一个空 segment 并附加 RST 标志位。
- `_set_rst`,设置错误状态,标志整个 TCP 链接终止
实现内容主要有两个部分,一个是链接终止的处理,另一个是组合封装 Sender 和 Receiver。
**组合封装:** 如果不考虑连接终止处理的话,这些操作的写法大概是这样的。
- `connect`:发起连接请求,如果不考虑用户瞎调用的情况,那么直接调用 `fill_window` 就可以了,因为默认的第一个包会生成一个单独的 SYN。
- `write`:写入数据并尽可能发送。直接把数据塞进 sender 的字节流里面,然后 `fill_window + send_all`
- `tick`:调用 sender 的 tick记得要发包因为 sender 可能有重传
- `segment_received`
- 先把收到的 segment 塞进 receiver 里面;
- 然后判断是不是一个 ACK因为需要通知 sender这里需要特判是否连接已经建立不然可能会导致 listen 的时候提前发送 SYN`ack_received` 会调用 `fill_window`,如果对面没连上就 ACK那 server 就会主动发 SYN这显然不对
- 最后需要判断是否需要发一个响应,两种情况:一种是对面发了数据过来,另一种是对面发了 TCP Keep-Alive。这两种都需要发送至少一个包来更新 ackno 和 win不过如果前面 sender 有发过包了,就不需要特地加一个空包了。
- 在处理完这一切之后,记得 `send_all`
- `end_input_stream`:发一个 FIN直接调用 sender 的字节流的 `end_iuput`,然后记得 `fill & send`
- 其他的接口就一行,看着名字像就把 sender 和 receiver 的接口转发就好了。
**连接终止:** 这是新加的部分,框架设计者选择把它塞进 Connection 部分实现。
讲义上面的四个前提1. inbound 全部收到并且对面已经通知我们终止(我们收到了 FIN 2. outbound 终止并且全部发完(我们发了 FIN 过去); 3. outbound 发过去的东西全部收到了 ACK 4. 本地连接相信对面也满足3也就是对面也全部收到了我们的 ACK
这里主要的问题在于,我们不知道对面有没有收到我们发过去的 ACK。如果 ACK 掉了,那么对面就会重传;如果这个时候我们没有跑路,那还可以重新发个 ACK如果我们跑路了那对面就只能一直重传直到 RST这显然是很浪费的。所以需要我们 linger 一段时间,如果这段时间里面对面没有发重传,那么可以认为对面收到了。这段时间需要足够长,以至于对面能够超时重传。
不需要等待的情况就是#4我们能够保证对面一定会收到我们所有的ACK也就是对面发了个 FIN然而我们还有东西要发那么对面一定会收到我们捎带的 ACK否则我们会重传因为我们的 ACK 有数据啊)。
具体的做法如下:
- 增加状态:`_time_since_last_segment_received` 用来记录自从上次的收到 segment 之后过了多久,看看对面有多久没有回信了;`_linger_after_streams_finish` 用来表示是否需要在本地结束之后再等一段时间;`_active` 表示当前连接是否终止,默认是 true但是其实可以在 `connect` 之后再改成 true不过好像无所谓
- 两个相关的接口:
- `segment_received` 需要重置 `_time_since_last_segment_received`,判断是否不需要 linger最重要的、如果收到了对面的 RST、那我们也需要立刻终止
- `tick` 需要更新 `_time_since_last_segment_received`,在 sender tick 过后判断重传计数、如果太多就发个 RST 跑路,最后写一长串条件判断连接 linger。
- 判断条件:
- linger 条件讲义描述的是“inbound 在 outbound EOF 之前结束则置为 false这里指的是 outbound 没发送 FIN否则默认 linger。这个 false 的条件可以从前面的状态机里面找, `sender=SYN_ACKED|SYN_ACKED(also) && recver=FIN_RECV`
- 终止条件:首先判断是否满足#1和#3也就是`sender=FIN_ACKED && recver=FIN_RECV`;然后判断是否需要 linger如果需要那么还需要判断 `_time_last_segment_received >= 10 * _cfg.rt_timeout`
Implementation Challenges:
CNM垃圾测试前面的代码写错了居然测不出来老子抓 tshark 抓了半天发现 FIN 发完之后的序列号居然是错的,因为当时根本没测 `send_empty_segment` 的序号到底对不对,然后当时不知道为啥脑子抽了写了个 `_next_seqno - 1` 进去,然后 FIN 的序号就一直少一个,直接把对面给整不会了。然后这个 bug 在两边都是自己写的协议的情况下还是对的,因为两边都认为是这样;直到和 Linux 的协议栈对测的时候才测出来,真的离谱。这个破玩意浪费了我两小时,艹。
Remaining Bugs:
大概是没有了吧,毕竟都能 `webget` 了。
- Optional: I had unexpected difficulty with: [describe]
- Optional: I think you could make this lab better by: [describe]
- Optional: I was surprised by: [1.前面的测试居然有的测不出来; 2.auto 有点不靠谱啊,返回的引用居然直接给老子复制了,必须写 `auto&` 才能得到引用,不然所有的更改都不会反映到内部结构上]
- Optional: I'm not sure about: [Keep Alive 好像可以不管来着]