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
配环境遇到好几个问题:
- cmake 版本不够,使用
pip install
升级:https://stackoverflow.com/a/52265521/14380036 - 在 Windows 内
git clone
,在 wsl 内 cmake,导致报错
解决:git clone 也要在 wsl 内完成no such file or directory CMake Error: Generator: execution of make failed. Make command was: /mnt/c/minnow/scripts/make-parallel.sh -f Makefile
- 使用 g++11 编译报错:
使用 g++12 解决并设置环境变量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"; }
CXX
解决,参考 networking - How can I replace gcc with g++-11 for C++20 when running cmake - Stack Overflow - 安装并使用 g++12
在 linux 中,多个版本的 g++ 是共存的,编译命令中的g++
是通过符号链接(symbolic link)到对应的版本,可以通过
which g++
看到g++
被符号链接到了谁,这个符号链接链接了很多次
pdf 提到了最后是使用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
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
- 针对 last 需要 bool 保护特判,不能直接初始化成 0,在我们不知道 last index 是什么的时候,记录一个变量
know_last
为 false
实在是做不到 10Gbits
checkpoint2
bugs:
-
SYN 携带的 data 也要写入 reassembler。
因为理论上,SYN 不应该携带 payload,因为需要得到对方的 ack 后才可以发送数据
-
FIN 会携带数据
-
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 长这样:
- 这段 data 被 SYN 和 FIN 包裹,因为 SYN 和 FIN 占据 sequence space
- 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 :
每次 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
这一问是考察收包丢包的独立性,感觉像贝叶斯,逻辑是这样的:
-
所有收到的包就可分成两种:
- 前一个包是丢的
- 前一个包是收到的
-
每一个应该收到的 echo reply 包,可能收到了,也可能丢了
\(count(此包收到) = count(前一个包收到,此包也收到) + count(前一个包丢了,但是此包收到了)\)
在我自己的实验过程中,是后者占绝大多数。
checkpoint5
通过 ARP 报文查询 ip 对应的 eth_addr 时候,ARP 报文中的 dst_eth_addr 字段为 0,而封装好的 frame 的 dst_eth_addr 字段为全 1
要先 queue 不知道 ip 地址的 datagram,再发送 arp 报文
checkpoint6
无
checkpoint7
wsl 把代理关了是可以做的