CS144 lab2

在lab2中将实现tcp的接收器

project1

实现warp和unwrap,进行在序号和绝对序号之间的转换

一个数据的序号可能很大,但是在tcp中能够使用的序号是有限的,只有uint32, 所以当一个数据的大小超过uint32的时候,将会进入到0
进行循环,所以序号是循环的。

warp的的功能在于将起始的序号加上相对序号变为tcp内的序号

1
2
3
WrappingInt32 wrap(uint64_t n, WrappingInt32 isn) {
return WrappingInt32{static_cast<uint32_t>(n) + isn.raw_value()};
}

unwrap的功能在于将tcp内的序号转换到初始的序号,实验中给了相对序号n,初始序号ISN,和最近的一个点序号
那么可以先将最近的一个点wrap成相对序号,得到和n之间的差距,然后加上原来的和n之间的距离即可。

1
2
3
4
5
uint64_t unwrap(WrappingInt32 n, WrappingInt32 isn, uint64_t checkpoint) {
int32_t steps = n - wrap(checkpoint, isn);
int64_t res = checkpoint + steps;
return res >= 0 ? checkpoint + steps : res + (1UL << 32);
}

project2

实现segment_received 和ackno两个方法。

在发送的数据段结构如下所示。
seg

SYN表示此时开始,fin表示此时结束,当没有开始的时候不进行接收,此时还需要得到绝对的数据,直接调用上面的函数即可,checkpoint设置为已经读入的数据。
isn为开始的时候的段中的seqno

1
2
3
4
5
6
7
8
9
10
11
12
13
void TCPReceiver::segment_received(const TCPSegment &seg) {
TCPHeader header = seg.header();
if (!header.syn&&!_syn) return;
if (header.syn&&_syn) return;
if (header.syn) {
_syn = 1;
_isn = header.seqno.raw_value();
}
// syn为开始,fin为结束
uint64_t checkpoint = _reassembler.stream_out().bytes_written() + 1;
size_t absolute_seqno = unwrap(header.seqno, WrappingInt32(_isn),checkpoint);
_reassembler.push_substring(seg.payload().copy(), header.syn ? 0 : absolute_seqno -1 ,header.fin);
}

ackno中返回下一个需要的数据的开始序列号

1
2
3
4
5
6
7
8
optional<WrappingInt32> TCPReceiver::ackno() const {
if (!_syn) return nullopt;
uint64_t abs_ack_no = _reassembler.stream_out().bytes_written() + 1;
if (_reassembler.stream_out().input_ended()) {
++abs_ack_no;
}
return WrappingInt32(_isn + abs_ack_no);
}