overview
这个lab需要我们实现TCP协议中的接收方,具体的,负责实现确认(acknowledgement)和流控制(flow control)。
64bit index - 32bit seqno
第一个部分,需要我们实现将上一个lab里面stream的index转换为TCP报文的序列号,指导书给出了序列号的特点:
- 序列号只有32位,属于阿贝尔群
- 序列号并不一定从0开始,可以是随机数。第一个序列号被称为ISN(initial sequence number)。
- 对于每个stream,在其前面的SYN flag和FIN flag也是需要使用序列号的。
为了方便,我们定义了64位绝对序列号,第一个序列号对应到绝对序列号为0,以此类推。而对于一个stream,其index就等于绝对序列号-1(SYN占据了0的位置)。
wrap
实现了绝对序列号到序列号的转换
unwrap
实现了序列号到绝对序列号的转换
因为序列号可能对应多个绝对序列号,所以需要找到离checkpoint最近的绝对序列号,每个比较都是采用绝对值,是为了防止边界情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| Wrap32 Wrap32::wrap( uint64_t n, Wrap32 zero_point ) { return zero_point + static_cast<uint32_t>(n); }
uint64_t Wrap32::unwrap( Wrap32 zero_point, uint64_t checkpoint ) const { uint64_t offset = static_cast<uint64_t>(raw_value_ - zero_point.raw_value_); uint64_t t = offset + (checkpoint & 0xffffffff00000000); uint64_t dis1 = (t + (1UL << 32) > checkpoint) ? (t + (1UL << 32) - checkpoint) : (checkpoint - t - (1UL << 32)); uint64_t dis2 = (t > checkpoint) ? (t - checkpoint) : (checkpoint - t); uint64_t dis3 = (t - (1UL << 32) > checkpoint) ? (t - (1UL << 32) - checkpoint) : (checkpoint - t + (1UL << 32)); uint64_t ans; if(dis1 < dis2){ if(dis1 < dis3) ans = t + (1UL << 32); else ans = t - (1UL << 32); } else { if(dis2 < dis3) ans = t; else ans = t - (1UL << 32); } return ans; }
|
TCP receiver
这里需要我们正式开始实现接收方的动作。先把类中的成员丢在这里,下面解释。
1 2 3 4 5 6 7 8 9
| class TCPReceiver { ... private: Reassembler reassembler_; Wrap32 ISN {0}; bool ISN_recv {false}; uint64_t ack_abseqno {}; };
|
指导书上只是讲了一些原理,但有一些东西没讲清楚。
- 要注意我们需要发送的ackno是根据已接收并且已经按顺序排好的字节来设定的,需要使用first_unassemble_byte的位置来确定。
- 特别注意,每个stream都有一对SYN和FIN,都占序列号,不要忘记将他们加到ackno里面。
其他的实现要素在指导书上都有,多读几遍,多看调试的信息就可以知道程序的行为了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| void TCPReceiver::receive( TCPSenderMessage message ) { if(message.RST){ reassembler_.reader().set_error(); return; }
if(message.SYN){ ISN = message.seqno; ISN_recv = true; reassembler_.insert(0, message.payload, message.FIN); ack_abseqno += message.sequence_length(); return; }
if(!ISN_recv) return;
uint64_t first_unindex = reassembler_.writer().bytes_pushed(); reassembler_.insert(message.seqno.unwrap(ISN, first_unindex)-1, message.payload, message.FIN); ack_abseqno = reassembler_.writer().bytes_pushed() + 1; if(reassembler_.writer().is_closed()) ack_abseqno += 1; }
TCPReceiverMessage TCPReceiver::send() const { TCPReceiverMessage t; if(ISN_recv){ t.ackno = Wrap32::wrap(ack_abseqno, ISN); } if(reassembler_.reader().has_error()) t.RST = true;
t.window_size = (reassembler_.writer().available_capacity() > UINT16_MAX) ? UINT16_MAX : reassembler_.writer().available_capacity(); return t; }
|