Lab汇总
概述
利用lab0和lab1实现的ByteStream
类和StreamReassembler
类实现一个
T
C
P
R
e
c
e
i
v
e
r
TCPReceiver
TCPReceiver,即
T
C
P
TCP
TCP的接收功能。
Part1 真实序号与32位序号的相互转换
该部分主要实现StreamReassembler
中64位的序号(从零开始)与
T
C
P
TCP
TCP头部中32位的序号(从某个随机初始序号开始)的相互转换。
将序号视为一个32位无符号整数(使用自然溢出方法对2的32次方取模)
一个字节流的初始序号对应着SYN
标记,末尾序号对应着FIN
标记,除去SYN
与FIN
,中间是真正的字节内容(也就是说SYN
与FIN
都占有着一个序号,即使报文中的数据部分没有数据)
比如文档中给出的例子(初始序号isn
为2^32-2):
其中,
seqno
就是TCP报文中的序号字段(32位无符号数)absolute seqno
是原字节流加上SYN与FIN之后的序号(从0开始递增)stream index
是原字节流的序号(不包括SYN和FIN,从0开始递增)
详细的字段描述如下图所示
具体实现
要求实现以下两个函数:
-
wrap
函数
该函数将absolute seqno转换为seqno,很简单,直接将64位无符号整数截取到32位无符号整数再加上初始序号(isn)即可 -
unwrap
将给定的seqno
转换为与某个给定值checkpoint
(是一个absolute seqno
)距离最近的一个absolute seqno
,这个也不难(但是我很傻搞了一上午)
我的思路如下(可能不太严谨)
令给定的seqno
的值为 n n n,checkpoint
为 c k ck ck,则checkpoint
转换为相应的seqno
为 c k ′ = ( c k % 2 32 + i s n ) % 2 32 ck'=(ck \% 2^{32} + isn) \% 2^{32} ck′=(ck%232+isn)%232
假设 n n n转换成absolute seqno
之后的值为 n ′ n' n′,文档要求( n ′ n' n′与 c k ck ck之间的间隔)与( n n n与 c k ′ ck' ck′之间的间隔)一致。
那么首先算出 n n n与 c k ′ ck' ck′之间的间隔 d i f f = ( n − c k ′ + 2 32 ) % 2 32 diff=(n - ck' + 2^{32})\% 2^{32} diff=(n−ck′+232)%232, d i f f diff diff也会等于 ( n ′ − c k + 2 32 ) % 2 32 (n' - ck + 2^{32}) \% 2^{32} (n′−ck+232)%232
即 d i f f + c k diff + ck diff+ck 与 n ′ n' n′ 在 2 32 2^{32} 232下同余
就是说 n’ = d i f f + c k + k ∗ 2 32 diff + ck + k * 2^{32} diff+ck+k∗232( k k k为任意整数)
由于需要求出与 c k ck ck距离最近的一个 n ′ n' n′,于是要么是 c k + d i f f ck + diff ck+diff要么是 c k + d i f f − 2 32 ck + diff - 2^{32} ck+diff−232
还有一个细节就是, c k + d i f f ck + diff ck+diff不用考虑溢出问题,但是 c k + d i f f − 2 32 ck + diff - 2^{32} ck+diff−232 也就是 c k − ( 2 32 − d i f f ) ck - (2^{32} - diff) ck−(232−diff)可能会下溢出,此时就只能用 c k + d i f f ck + diff ck+diff了
参考代码如下:
uint64_t unwrap(WrappingInt32 n, WrappingInt32 isn, uint64_t checkpoint) {
const uint32_t diff = static_cast<uint32_t>(n - wrap(checkpoint, isn));
if (diff < (1U << 31)) return checkpoint + diff;
else {
uint64_t res = checkpoint - ((1UL << 32) - static_cast<uint64_t>(diff));
if (res > checkpoint) res = checkpoint + diff;
return res;
}
}
Part2 实现 T C P R e c e i v e r TCPReceiver TCPReceiver
当收到一个
T
C
P
TCP
TCP的报文段时,
R
e
c
e
i
v
e
r
Receiver
Receiver只关注
T
C
P
TCP
TCP报文段头部的mermaid sequenceDiagram Number
(seqno
)、SYN
标志位、FIN
标志位和Payload
数据部分
需要实现的函数有:
-
segment_received
该函数为接收到一个 T C P TCP TCP报文段时, T C P R e c e i v e r TCPReceiver TCPReceiver的处理接口
如果当前 T C P r e c e i v e r TCPreceiver TCPreceiver还未收到一个带有SYN
标志的报文,且当前收到的报文中不带SYN
标志,则丢弃该报文
当收到的段中带有SYN
标志时,需要保存一下initial sequence number
只要已经收到带SYN
标志的报文,则需要将数据部分调用 S t r e a m R e a s s e m b l e r StreamReassembler StreamReassembler的push_substring
方法将数据放入装配器中
当报文带有FIN
标志时,数据部分的最后一个字节即是该字节流的EOF
调用push_substring
方法时,需要将seqno
转换为stream index
(调用unwrap
函数然后将结果减一即可);如果当前报文带有SYN
标记,数据部分首字节的seqno
需要加一;根据文档,unwrap
函数的checkpoint
选择最后一个reassembled
的字节的absolute seqno
-
ackno
该函数的功能为返回当前的acknowledge number
注意加上SYN
和FIN
占有的两个字节就可以啦 -
window_size
返回当前缓存窗口(暂未放入接收窗口的比特范围)大小(容量减去当前接收窗口中的字节数)