CS144:Lab2

写在前面

本次实验是实现一个TCP receiver。对于一个TCP的接收方,需要接受来自发送方的TCP数据报,并根据数据报的头部信息,将数据报的载荷整合到bytestream中。用于整合数据报载荷的头部信息有三部分,分别是数据报的seqno,syn以及fin。该实验主要有两部分内容组成。

数据报中头部的seqno包括了syn和fin的相对序列号,为了避免网络中残余的数据报的影响,TCP发送方会为syn随机初始化一个序列号,称其为ISN。seqno是一个长度为32比特的无符号整型,当字节seqno超出了该类型的长度,会从0开始重新计数。绝对seqno是一个64比特的无符号整型,该类型基本上可以涵盖发送方要发送的所有的字节,并且绝对seqno的下标是从0开始计数。字节流的index也是从0开始编号,但是不包括syn和fin,仅仅指代有效的载荷。重组器用的编号是字节流的index,因此需要将数据报的seqno转换成绝对的seqno再转化成字节流的index。从绝对的seqno转化为字节流的index只需要将前者减一即可。而前两者之间的转换会很复杂。该实验第一部分就是实现这一块的内容。

该实验的第二部分是实现TCP receiver类。主要的任务是将收到的数据报中的载荷通过重组器重组进bytestream,并且返回ackno。ackno指的是TCP receiver需要接受的下一个字节的序列号,即滑动窗口最左边的字节的编号。

实验部分

一、Translatint between 64-bit indexes and 32-bit seqno

这一部分正如上述所说,是相对序列号与绝对序列号之间的转换。以下提供我的实验思路。

1.1 wrap

该函数是给定一个绝对序列号和ISN,将绝对序列号转换为相对序列号。由于相对序列号的表示范围是0到2^{32}-1,因此将绝对序列号除上2^{32}得到的余数作为offset,加到ISN上即可。代码如下:

WrappingInt32 wrap(uint64_t n, WrappingInt32 isn) {
    uint32_t off=n%(1ll<<32);
    return WrappingInt32{isn.raw_value()+off};
}

1.2 unwrap

该函数是给定一个相对序列号,ISN和一个checkpoint,计算绝对序列号。首先根绝相对序列号和ISN的大小获得偏差offset。如果offset大于等checkpoint,则绝对序列号就是checkpoint;否则绝对序列号是离checkpoint最近的那一个。代码如下:

uint64_t unwrap(WrappingInt32 n, WrappingInt32 isn, uint64_t checkpoint) {
    uint64_t off,x,res;
    if(n.raw_value()>=isn.raw_value()) off=n.raw_value()-isn.raw_value();
    else off=static_cast<uint64_t>(n.raw_value())+
	    ((1ll<<32)-static_cast<uint64_t>(isn.raw_value()));
    if(off>=checkpoint) return off;
    x=(checkpoint-off)/(1ll<<32);
    res=off+x*(1ll<<32);
    if((checkpoint-res)>(1ll<<31)) res+=1ll<<32;
    return res;
}

二、Implementing the TCP receiver

TCP receiver类中只需要再定义一个optional<WrappingInt32> ISN=nullopt,用来记录isn编号,目的是为了后续的seqno的转换。该类需要实现3个函数,分别是segment_received(),ackno(),window_size()。每个函数的实现思路如下:

  • void TCPReceiver::segment_received(const TCPSegment &seg):该函数先分别获得TCP数据报的载荷、头部,再从头部中提起syn,fin和seqno的信息。先判断TCP数据段是不是syn,如果是就将记录下isn编;再判断是不是fin,若是将eof置为true。之后用_reassembler传输数据,如果该TCP receiver没有收到过syn数据报但是收到了其他的数据报,表明该数据报是无效的,不进行任何的处理;否则需要对数据报进行处理。数据报中可能会出现两种情况,一种是syn+data,另一种是只有data。如果是前者,数据报头部的seqno信息指的是syn标记,data的seqno需要对其加1。之后再对seqno转换成bytestream的index,其中checkpoint选择的是滑动窗口最左端的字节编号,即bytestream中总共写入的字节数。

  • optional<WrappingInt32> TCPReceiver::ackno() const:该函数返回的是下一个需要接受的字节编号。如果TCP接收方还未收到syn数据报,即ISN==nullopt,那么就返回nullopt即可。否则需要返回一个序列号。这里有两种情况,一是字节流已经到达了eof,此时由于fin也占用一个字节号,需要额外加1;其他情况只需要将bytestream中总共写入的字节数+1作为绝对seqno进行转化即可。

  • size_t TCPReceiver::window_size():该函数是需要返回滑动窗口的大小,即在总的容量-在字节流中的字节数。

具体的代码如下:

void TCPReceiver::segment_received(const TCPSegment &seg) {
    TCPHeader header=seg.header();
    string data=seg.payload().copy();
    bool syn=header.syn,fin=header.fin,eof=false;
    uint64_t checkpoint,index;
    WrappingInt32 seqno{0};
    if(syn){
	    ISN=header.seqno;
    }
    if(!ISN) return; 
    if(fin){
	    eof=true;
    }
    //convert seqno of data to absolute seqno
    //checkpoint is the left index of sliding window.
    seqno=header.seqno;
    if(syn) seqno=seqno+1;
    checkpoint=_reassembler.stream_out().bytes_written();
    index=unwrap(seqno,ISN.value(),checkpoint)-1;
    _reassembler.push_substring(data,index,eof);
}

optional<WrappingInt32> TCPReceiver::ackno() const { 
	if(!ISN) return nullopt;
    WrappingInt32 ackno=
            wrap(_reassembler.stream_out().bytes_written()+1,ISN.value());
    if(_reassembler.stream_out().input_ended())
	    ackno=ackno+1;
	return ackno;	
}

size_t TCPReceiver::window_size() const { 
    return _capacity-_reassembler.stream_out().buffer_size(); 
}

三、实验结果

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值