CS144 2024 winter 作业笔记

CS144 2024 winter 作业笔记
cs144 homework notes

Created: 2024-05-14T10:24+08:00
Published: 2024-07-15T20:16+08:00

Categories: ComputerNetwork

课程网站: CS 144: Introduction to Computer Networking
我的代码: rfhits/cs144-2024-winter

checkpoint0

配环境

sudo apt update && sudo apt install git cmake gdb build-essential clang clang-tidy clang-format gcc-doc pkg-config glibc-doc tcpdump tshark

配环境遇到好几个问题:

  1. cmake 版本不够,使用 pip install 升级:https://stackoverflow.com/a/52265521/14380036
  2. 在 Windows 内 git clone,在 wsl 内 cmake,导致报错
    no such file or directory
    CMake Error: Generator: execution of make failed. Make command was: /mnt/c/minnow/scripts/make-parallel.sh -f Makefile
    
    解决:git clone 也要在 wsl 内完成
  3. 使用 g++11 编译报错:
    error: invalid return type ‘std::string’ {aka ‘std::__cxx11::basic_string<char>’} of ‘constexpr’ function ‘static constexpr std::string ExpectationViolation::boolstr(bool)’
    16 |   static constexpr std::string boolstr( bool b ) { return b ? "true" : "false"; }
    
    使用 g++12 解决并设置环境变量 CXX 解决,参考 networking - How can I replace gcc with g++-11 for C++20 when running cmake - Stack Overflow
  4. 安装并使用 g++12
    在 linux 中,多个版本的 g++ 是共存的,编译命令中的 g++ 是通过符号链接(symbolic link)到对应的版本,可以通过
    which g++ 看到 g++ 被符号链接到了谁,这个符号链接链接了很多次
    user@unix:~$ which g++
    /usr/bin/g++
    user@unix:~$ file /usr/bin/g++
    /usr/bin/g++: symbolic link to g++-11
    user@unix:~$ which g++-11
    /usr/bin/g++-11
    user@unix:~$ file /usr/bin/g++-11
    /usr/bin/g++-11: symbolic link to x86_64-linux-gnu-g++-11
    user@unix:~$ which x86_64-linux-gnu-g++-11
    /usr/bin/x86_64-linux-gnu-g++-11
    user@unix:~$ file /usr/bin/x86_64-linux-gnu-g++-11
    /usr/bin/x86_64-linux-gnu-g++-11: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=f97745c2db747184cd62fd36ecb924046f3d725f, for GNU/Linux 3.2.0, stripped
    
    pdf 提到了最后是使用 g++ v13.2 测试的,所以最好不要用 clang 编译

weget

把这些 write 到 socket 里面就好了

GET /hello HTTP/1.1
HOST: cs144.keithw.org
Connection: close

最后应该要 shutdown,这部分可以参考 Lecture 02 的 TCP state。

reliable byte stream

用 string 配合 begin end 和 replace 实现的,减少 erase 和 append,可以实现 10+ Gbit/s

本来想是写成 ring-buffer,结果发现 peek() 要返回 string_view,可见还是要通读需求再开始设计。

疑问

cmake --build build --target tidy 虽然是 optional,但是执行的时候报错 -Wno-unqualified-std-cast-call,也不知道怎么解决。

checkpoint1

  1. 针对 last 需要 bool 保护特判,不能直接初始化成 0,在我们不知道 last index 是什么的时候,记录一个变量 know_last为 false

实在是做不到 10Gbits

checkpoint2

bugs:

  1. SYN 携带的 data 也要写入 reassembler。

    因为理论上,SYN 不应该携带 payload,因为需要得到对方的 ack 后才可以发送数据

  2. FIN 会携带数据

  3. receiver 需要记录状态,比如是否开始(SYN),是否结束(FIN),sender() 需要使用状态和 reassember.bytes_pending 决定 ackno。
    如果收到了 FIN,并且没有 pending bytes,ackno 要考虑 FIN 占据了一个 seqno,如果有 pending bytes,不需要考虑 FIN。

checkpoint3

model of input_

一开始我就犯了一个错误并且写了很久导致时间的浪费。

这个错误就是,Sender 的成员 ByteStream 可能是没有关闭的,在 Sender 的视角里,要发送的 Data 长这样:

  1. 这段 data 被 SYN 和 FIN 包裹,因为 SYN 和 FIN 占据 sequence space
  2. FIN 可能还没有出现,SYN 可能还没有发送

Sender.push() 从 bytes in stream 中取出 window size 允许大小的数据,然后发送。读取的方法必须调用 pop(),不然 input_的写端可能没法往里面再写数据了,这里课程组提供了 read() 方法:void read( Reader& reader, uint64_t len, std::string& out )

关于 input_ 只会使用到 3 个接口:

reader.bytes_buffered(): how many bytes not send
reader.is_finished()
void read( Reader& reader, uint64_t len, std::string& out )

将要发送的数据视为 FIN + payload + FIN,这是本科的计算机网络教学没有提及的。

Sender

sender 维护了 segment in flight,每次 push 都只是发送还没发送的 segment,不需要重新发送 segment in flight。因为 segment in flight 可以被 timer 超时重传。

- abs_last_ackno    : uint64 # promise aligned with outstanding segments
- abs_exp_ackno     : uint64 
- wnd_size          : uint64

- ost_segs	        : set<uint64, msg>
- retx_cnt          : int # retransmit count
- is_last_retx      : bool
- cur_rto           :

Sender.push()

每次 receive 时候,就是 Receiver 告知 Sender 自己可以接受的数据边界,Sender 根据它来调整自己的窗口大小。

注意,Sender 并非调整自己的窗口 message 中声明的窗口大小,message 只是告知 Sender:Receiver 现在可以处理的数据边界为 msg.ackno + msg.window_size,请把在此边界内的数据全部发送过来。这也是本科学习计算机网络的一个盲区。

Sender 根据 last_ackno 调整窗口大小,如果边界内的数据已经发送了,就不需要再发送,如果还可以发送新的,就要接着从 ByteStream 中取出数据发送。

调整窗口的公式为:last_ackno + sender_window_size = rcv_ackno + rcv_window_size

timer

- bool is_running
- bool is_expired
- uint64 cur_time # remain time
- uint64 exp_time # expire time

timer 的作用:定期检查是否有 segment in flight,如果是,expire 的时候重新发送。

所以发送有 sequence space 的 segment 时候,如果 timer 不在运行,就要启动 timer。

tick: 检查 timer 是否超时,如果是,触发检查。检查发现有 segment in flight,重传并重启 timer;如果没有 segment in flight,清除 timer。

receive:如果收到了新的segment,原来的 timer 就要作废。再检查是否有 segment in flight,如果有就 timer 重启,否则清除 timer。

Sender 和 Receiver 交互的流程

# first time, push a SYN
Sender.push() -- (SYN) --> Receiver

# receive data
Sender.receive() <-- (ACK, window_size, ackno) -- Receiver

# fill data into window and send to receiver
Sender.push() -- (data) --> Receiver

# periodically tick, check timer, for retransmit
Sender.tick() -- (data) --> Receiver

checkpoint4

因为 wsl 没法做,试了半天 CentOS,放弃。最后用 VMware 装虚拟机做了,而且 VMware v17 也是对个人免费的。

发现自己收到的 echo reply seqno 不是连续的,很有规律地 +3。如果把 echo request 的速度改为一秒一次,sequence number 就是连续的,说明 0.2s 发一次,会太快导致丢包。

How independent or correlated is the event of “packet loss” over time? In other words:
• Given that echo request #N received a reply, what is the probability that echo request #(N+1) was also successfully replied-to?
• Given that echo request #N did not receive a reply, what is the probability that echo request #(N+1) was successfully replied-to?
• How do these figures (the conditional delivery rates) compare with the overall “unconditional” packet delivery rate in the first question? How independent or bursty were the losses

这一问是考察收包丢包的独立性,感觉像贝叶斯,逻辑是这样的:

  1. 所有收到的包就可分成两种:

    1. 前一个包是丢的
    2. 前一个包是收到的
  2. 每一个应该收到的 echo reply 包,可能收到了,也可能丢了

\(count(此包收到) = count(前一个包收到,此包也收到) + count(前一个包丢了,但是此包收到了)\)

在我自己的实验过程中,是后者占绝大多数。

\[\frac{count(此包收)}{包总数} = \frac{count(前包收,此包收)}{总包数} + \frac{count(前包丢,此包收)}{总包数} \\ count(前包收,此包收) = 收包数 \times P(已知前包收,后包也收) \\ count(前包丢,此包收) = 丢包数 \times P(已知前包丢,后包却收) \\ \frac{count(此包收)}{包总数} \\ = P(收包) \\ = \frac{count(前包收,此包收)}{总包数} + \frac{count(前包丢,此包收)}{总包数} \\ = P(收包) \times P(已知前包收,后包也收) + P(丢包) \times P(已知前包丢,后包却收) \]

checkpoint5

通过 ARP 报文查询 ip 对应的 eth_addr 时候,ARP 报文中的 dst_eth_addr 字段为 0,而封装好的 frame 的 dst_eth_addr 字段为全 1

要先 queue 不知道 ip 地址的 datagram,再发送 arp 报文

checkpoint6

checkpoint7

wsl 把代理关了是可以做的

posted @ 2024-07-15 20:20  ticlab  阅读(33)  评论(0编辑  收藏  举报