概述
- Lab0和Lab1的代码已经完成了大多数的逻辑,Lab2将使用它们,实现一个TCPReceiver,难点在于每个字节的序号
- TCPReceiver将incoming TCP segments转化为incoming byte stream
- TCPReceiver通过segment_received()方法从网络层接收segments,调用StreamReassembler的相关方法,最终写入ByteStream
- 应用程序从这个ByteStream中读取数据
- TCPReceiver还负责告诉发送方
- ACK,下一个待接收的起始字节序号
- 接收窗口大小,“first unassembled”的字节和“first unaccepted”的字节的序号之间的差值
- ACK和接收窗口大小共同描述了接收窗口,即接收方允许发送方发送的数据范围,实现了发送方的流量控制
- ACK就是接收窗口区间的左端点(闭)
- ACK+接收窗口大小就是接收窗口区间的右端点(开)
编写代码前的准备
你应该在Lab0和Lab1的Sponge代码库的基础上实现本实验
- 确保在此之前已经commit了Lab1的内容,之后就不要再修改webget.cc、libsponge顶层目录下的文件可
- 在项目目录下运行git fetch,确保代码是最新的
- 运行git merge origin/lab2-startercode
- cd build
- make -j4
- writeups/lab2.md是本实验的checkList,需要提交,请认真填写
64位index与32位seqnos的转化
- 之前实现的StreamReassembler使用64位整数表示字节的stream index,从0开始计数
- 但是TCP头部中,这个序号是32位的,且从一个随机值开始计数(TCP sequence number)
- Your implementation needs to plan for 32-bit integers to wrap around(循环). 2^32字节只有4GB,一旦序列号达到了2^32-1,那么下一个字节的序列号就要从0开始
- TCP的起始序列号(Initial Sequence Number,ISN)是一个随机值,很难被猜到或重复
- 这是为了提高安全性,且为了防止和当前通信双方较早前的通信数据产生混淆
- SYN和FIN代表字节流数据的开始和结束,所以也要占1个序列号
- SYN和FIN不属于字节流数据的一部分
- 它们本身也不是字节,它们只是在逻辑上占据了1个序列号
- 另一个概念“absolute sequence number”:从0开始计数,不循环计数
下面举一个例子,假设字节流数据为“cat”,则上述三种序号如下表所示

下表总结了这三种序号
- 在Absolute Sequence Number和Stream Index之间转换是简单的,只需要简单的+1或者-1
- 在TCP Sequence Number和Absolute Sequence Number之间转换就比较复杂,很容易写出BUG
- 我们已经实现了TCP Sequence Number对应的类WrappingInt32,其包装了uint32_t
- 使用uint64_t代表Absolute Sequence Number
- WrappingInt32中的转换函数需要你来实现(这一段有点看不懂)

你可以运行ctest -R wrap来对WrappingInt32进行单元测试
实现TCPReceiver
概述
TCPReceiver要实现
- 从对等实体(发送方)接收segments
- 使用StreamReassembler将segments组装成ByteStream
- 计算ACK和接收窗口长度,并将这两个数据用一个segment回传给对等实体
- 首先复习一个TCP segment的格式
- 下图中高亮的是本实验重点关注部分,蓝色的是由发送方写入,接收方读取并做出反应
- seqno
- SYN、FIN
- Payload
- 红色的接收方需要写入的
- 下图中高亮的是本实验重点关注部分,蓝色的是由发送方写入,接收方读取并做出反应

- TCPSegment(https://cs144.github.io/doc/lab2/class_t_c_p_segment.html)代表了TCP报文信息;
- TCPHeader(https://cs144.github.io/doc/lab2/struct_t_c_p_header.html)代表了TCP报文的首部信息
- length_in_sequence_space()计算了序号空间的长度
- SYN+FIN+payload,SYN和FIN分别占1个序号,payload占其字节数个序号
- https://cs144.github.io/doc/lab2/class_t_c_p_segment.html#a41eb3ff25fee485849fd38eb31c990d6
- 下面关注TCPReceiver的接口


我们已经在.hh文件中提前实现了TCPReceiver的构造函数、unassembled_bytes方法和stream_out methods方法,下面列举了你需要实现的部分
segment_received()
- 这是运行过程中被调用最频繁的方法:每次接收到新的segment,这个方法就会被调用
这个方法的需要做
返回ACK编号,optional
-
window_size()
-
TCPReceiver在整个连接生命周期中的演变
在整个连接的生命周期中,TCPReceiver会在几个状态中顺序变化
- waiting for a SYN(with empty ackno)
- in-progress stream
- a stream that’s finished
我们会测试你的TCPReceiver正确处理了segments,并且在这些状态中正确的演化,如下图所示(你在Lab4中才需要关注下图中的error state或者RST标志)
开发与调试建议
- 在tcp_receiver.cc中实现TCPReceiver的公有接口,你可以在tcp_receiver.hh中添加任何需要的私有成员
- 你可以在编译代码后运行指令make check_lab2进行测试
- 注意git的使用

- 注意代码的可读性
- 使用现代化的C++语法风格
- 如果产生一个segmentation fault
提交注意事项
- 仅修改libsponge顶级目录下的.hh和.cc文件,可以为对象添加私有成员,但不要修改公有接口
- 在提交前,请按顺序运行
- make format
- git status(确保commit了所有的改动)
- make
- make check_lab2
- 编辑writeups/lab2.md,文档中需要包含
- 程序设计结构
- 实现过程中遇到的挑战
- 未解决的BUG
- 提交指南:https://cs144.github.io/submit
