CS144 lab2 笔记

5 篇文章 0 订阅

CS144 lab2 笔记

介绍

lab0中,我们实现了一个ByteStream

lab1中,实现了一个重组字符片段的StreamReassembler,重组收到的字符片段,并且将排序好的字符串退送到ByteStream

lab2中,j将实现一个TCPReceiver,它将在TCP segmentsbyte stream之间进行转换

image-20220123222702799

通过这个图片

  • ackno就是第一个未排序片段的索引,是期望下一个收到的片段索引

  • 第一个未排序片段与流末端索引之间的距离就是window size(TCP窗口)

    the distance between the “first unassembled” index and the “first unacceptable” index.
    This is called the “window size”

Translating between 64-bit indexes and 32-bit seqnos

img

  1. 通过TCP报头我们可以知道,传输过程中的seq是32位的,但我们本地的seq是64位系统下的,所以我们需要将seq(64bit)--->seq(32bit),32位最大值为 232-1,超过这个数字就从0开始

  2. TCP seq 以一个32为随机值初始化:这个目的是为了防止被猜到,以及网络中较早的数据报造成干扰,一端连接中第一个seq就以一个32位的数字初始化,叫做Initial Sequence Number(ISN),之后每个seq/mod 232

  3. 连接开始和结束每个占用一个序列号:除了确保收到所有字节的数据,TCP确保流的开始和结束同样是是可靠的。因此,在TCP中,SYN(流开始)和FIN(流终端)控制标志被分配序列号。都占据一个序列号。 (SYN标志占用的序列号就是ISN。)流中的每个数据字节还占用一个序列号。请记住,SYN和FIN不是流本身的一部分,而不是“字节” —它们代表字节流本身的开始和结束。

这些seq在每个TCP段的头中发送。**绝对序列号:**始终以零开始并且不包装(就是64位),流索引:StreamReassEmbler流中的每个字节的索引,从零开始,64bit,具体见下图:

image-20220123224747548

思路

  • 很显然这种转换不是唯一的——seqno每次增加 2 32 2^{32} 232值都不变,但是absolute seqno变化。为了确定唯一的结果,我们需要checkpoint,即将可能的结果中距离checkpoint最近的作为最终结果。

  • checkpoint表示最近一次转换求得的absolute seqno,而本次转换出的absolute seqno应该选择与上次值最为接近的那一个。原理是虽然segment不一定按序到达,但几乎不可能出现相邻到达的两个segment序号差值超过INT32_MAX的情况

  • 如果想不出来,就在纸上画一画就能想出来了

实现

//! Transform an "absolute" 64-bit sequence number (zero-indexed) into a WrappingInt32
//! \param n The input absolute 64-bit sequence number
//! \param isn The initial sequence number
WrappingInt32 wrap(uint64_t n, WrappingInt32 isn) { return WrappingInt32(static_cast<uint32_t>(n) + isn.raw_value()); }

//! Transform a WrappingInt32 into an "absolute" 64-bit sequence number (zero-indexed)
//! \param n The relative sequence number
//! \param isn The initial sequence number
//! \param checkpoint A recent absolute 64-bit sequence number
//! \returns the 64-bit sequence number that wraps to `n` and is closest to `checkpoint`
//!
//! \note Each of the two streams of the TCP connection has its own ISN. One stream
//! runs from the local TCPSender to the remote TCPReceiver and has one ISN,
//! and the other stream runs from the remote TCPSender to the local TCPReceiver and
//! has a different ISN.
uint64_t unwrap(WrappingInt32 n, WrappingInt32 isn, uint64_t checkpoint) {
    int32_t interval = n - wrap(checkpoint, isn);
    int64_t result = checkpoint + interval;
    if (result >= 0)
        return result;
    else
        return result + (1ul << 32);
}

Implementing the TCP receiver

对于接收端,在这个实验中只需要处理示意图中彩色部分:

image-20220123231716604

在做实验之前最好看一下TCPSegment的相关实现

思路

主要就是参考下图中TCP的过程,将其分为图中三种情况,一一实现即可,错误不需要考虑,将在之后的实验进行处理

image-20220123232228395

segment received()
  • 请设置初始序列号。设置ISN,并且不要忘记使用 。请注意,SYN标志只是标题中的一个标志。相同的段也可以携带数据,甚至可以设置FIN标志。所以,即使受到syn也不能抛弃他的片段以及fin
  • 将任何数据或流终端标记推向StreamReasseMbler。如果FIN标志设置在TCPSegment的标题中,这意味着有效载荷的最后一个字节是整个流的最后一个字节。
ackno()
  • 返回包含接收器尚未知道的第一个字节的序列号的wraxingInt32。这是Window的左边缘。如果尚未设置ISN,则返回一个空可选
window size()
  • 见代码

实现

tcp_receiver.hh

class TCPReceiver {
    //! Our data structure for re-assembling bytes.
    StreamReassembler _reassembler;

    //! The maximum number of bytes we'll store.
    size_t _capacity;
    bool _syn {false};
    WrappingInt32 _isn{0};

tcp_receiver.cc

void TCPReceiver::segment_received(const TCPSegment &seg) {
    const TCPHeader &_tcp_header = seg.header();
    /*
     * 不需要考虑很多,直接按照tcp接收端状态转换图写就行,其他情况都可以交给reassembler处理
     */
    if (!_syn)  // 如果未收到过syn
    {
        if (!_tcp_header.syn)// 如果包中含有syn就继续,没有就直接返回,抛弃包
            return;
        _syn = true;
        _isn = _tcp_header.seqno;
    }
    // ack 期望下一个收到的片段索引
    uint64_t _ackno = _reassembler.stream_out().bytes_written() + 1;
    // 计算 seq
    uint64_t _seqno = unwrap(_tcp_header.seqno, _isn, _ackno);
    // 注意syn也占用seqno,所以别忘了
    uint64_t _index = _seqno - 1 + static_cast<uint64_t>(_tcp_header.syn);
    _reassembler.push_substring(seg.payload().copy(), _index, _tcp_header.fin);
}

optional<WrappingInt32> TCPReceiver::ackno() const {
    if (!_syn) // 如果未建立连接,直接返回空
        return nullopt;
    uint64_t _ackno = _reassembler.stream_out().bytes_written() + 1;
    if (_reassembler.stream_out().input_ended())// 如果结束连接,返回的ack要算上sender发来的fin
        _ackno++;
    return WrappingInt32(_isn) + _ackno;
}
// 如下
size_t TCPReceiver::window_size() const { return _capacity - _reassembler.stream_out().buffer_size(); }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值