1 介绍
Lab 2 需要完成 TCP Receiver 模块,跟lab0、lab1不同,TCP Receiver 和 TCP协议是相关的。
TCP Receiver 主要功能有:
ackno:表示希望发送者发送的第一个字节的seqnos
window size:表示第一个未重组的字节到第一个未接受字节的距离
2 实现64位 index到32位seqnos的转换
因为seqnos会包含在数据帧中,所以为了减少overhead,会采用32位的seqnos表示传输的序号,而32位并不能完全覆盖一次TCP传输所需,所以它seqnos会循环。我们采用64bit的index去记录传输比特流的绝对序号,(64bit足够覆盖一次TCP传输)。
起始的seqnos是一个随机值ISN,主要是为了安全性考虑,防止攻击者猜测出seqnos,然后进行数据传输。
ISN是在SYN发送过程中的seqnos,而SYN中不会发送数据流。所以真正的数据流的seqnos是从ISN + 1 开始的。同理结束信号FIN也会带一个seqnos,但是其中不含数据。
第一部分是实现index和seqnos的转换。
头文件中重载了一些操作符,使得我们编码的工作大大减少了。
unwrap相对会复杂一点。
因为 seqnos 会出现循环的情况,这边就通过一个checkpoint来定位。
头文件中有描述,选择里checkpoint近的index。
WrappingInt32 wrap(uint64_t n, WrappingInt32 isn) {
return isn + static_cast<uint32_t> (n);
}
uint64_t unwrap(WrappingInt32 n, WrappingInt32 isn, uint64_t checkpoint) {
uint64_t absolute_v = (checkpoint >> 32) << 32;
absolute_v += static_cast<uint64_t> (static_cast<uint32_t>(n - isn));
if(absolute_v > (static_cast<uint64_t >(1) << 32) && absolute_v > (checkpoint + (static_cast<uint64_t >(1) << 31)))
absolute_v -= static_cast<uint64_t >(1) << 32;
else if(absolute_v + (static_cast<uint64_t >(1) << 31) < checkpoint )
absolute_v += static_cast<uint64_t >(1) << 32;
return absolute_v;
}
3 Receiver
添加两个public方法
bool get_end_input_flag() const{ return end_input_flag; };
uint64_t getIndex() const{return _nowIndex;};
添加成员
WrappingInt32 _isn{0};
bool _isInit{false};
uint64_t expt_seq{0},checkpoint{0};
实现接口
主要是segment_received()的实现。
- 注意一个报文中可能同时会有SYN、FIN、payload。
- 处理上述的各种边界情况。
- 注意如果最后reassembler了EOF的字串,expt_seq需要加1,这表示FIN的那个字节。
void TCPReceiver::segment_received(const TCPSegment &seg) {
if(_isInit && seg.header().syn)
return;
if(!_isInit){
if(!seg.header().syn)
return;
_isn = seg.header().seqno;
_isInit = true;
if(seg.payload().size() == 0){
expt_seq = 1;
if(seg.header().fin){
_reassembler.push_substring("",0, true);
expt_seq++;
}
return;
}
checkpoint = 1;
}
else{
checkpoint = unwrap(seg.header().seqno,_isn,checkpoint);
}
const std::string data = seg.payload().copy();
const bool eof = seg.header().fin;
_reassembler.push_substring(data,checkpoint - 1,eof);
expt_seq = _reassembler.getIndex() + 1;
if(stream_out().get_end_input_flag()){
expt_seq ++;
}
}
optional<WrappingInt32> TCPReceiver::ackno() const {
if(!_isInit)
return nullopt;
return wrap(expt_seq,_isn) ;
}
size_t TCPReceiver::window_size() const { return stream_out().remaining_capacity(); }
|