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

5.7 KiB
Raw Blame History

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 的时候提前发送 SYNack_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 好像可以不管来着]