CS144 Lab2

CS144 Lab2

开始有点痛苦了,因为文档给的并不全面,光读文档是没法覆盖所有的test case的,有太多的业务逻辑要自己考虑。接下来分别就几个难点总结一下:

SYN/FIN

有几种情况要特判:

  • 当前没收到过SYN,要拒绝所有的segment
  • 当前收到过SYN了,又收到了新的SYN,这得拒绝
  • 当前收到了FIN,又收到了新的FIN,这也得拒绝

ack

根据定义,ack是receiver当前希望收到的下一个Byte编号,收到后就可以继续重组,这跟stream_reassembler里已经实现的reassembleByte(我自设的变量,若有需要见本博客 lab1 )定义是一样的,但ack又要考虑序列号,用 WrappingInt32(wrap(ack,ISN));方法包装一下ack就行(这个方法相当于return ack+ISN)。

另外,注意ack是包含SYN和FIN的,如果它们第一次出现,ack也要+1,否则测试点包过不去。

滑动窗口的计算

size_t winL = WrappingInt32(wrap(ack,ISN)).raw_value();
size_t winR = WrappingInt32(wrap(ack,ISN)).raw_value() + window_size() - 1;
size_t dataL = head.seqno.raw_value();
size_t dataR = head.seqno.raw_value()+lenData;
if(lenData) dataR -= 1;

滑动窗口的左边界就是ack,右边界是ack + window_size()-1,我的编译器里直接调ackno方法会报错,于是直接内联了(bushi)。数据的左边界同理,这里特判数据长度lenData是否为0,是因为如果不为0,dataR应该-1 ,但如果为0,再-1就导致 dataL > dataR ,这不合理。

注意segment_received方法要求判断数据是否在当前窗口里,只要有一部分在即可,所以不在的判断条件就是两个区间不相交:

if(dataL > winR || dataR < winL) {
     return false;
}

window_size()

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

放上这张经典图:
image

window_size的大小等于 "distance between first unassembled and first unacceptable",也即图中红色那段开始,到 first unacceptable 之间。即capcity(已知)-绿色段(总共写入的有序字节数(蓝+绿)-被取出的字节数(蓝))。最开始搞错了,写成 capcity-un_assembled,居然还过了好几个测试样例。

index

size_t index = unwrap(head.seqno,ISN,absSeq);// the absSeq of new data
index = index - 1;
if(SYN) index = 0;

根据pdf里的定义:
image

index = absSeq - 1,而absSeq又等于unwrap(head.seqno,ISN,absSeq),因为unwrap的作用就是找出从ISN出发,允许回绕的seq应该是多少。显然有很多seq都可以对应,我们挑一个离上次的absSeq最近的,这样能保证在mod (1<<32)的意义下,新的absSeq就是由上次的absSeq加上来的。

tcp_receiver.cc

#include "tcp_receiver.hh"
#include "stream_reassembler.hh"
#include<iostream>
#include <map>
// Dummy implementation of a TCP receiver

// For Lab 2, please replace with a real implementation that passes the
// automated checks run by `make check_lab2`.

template <typename... Targs>
void DUMMY_CODE(Targs &&... /* unused */) {}

using namespace std;

/*
This method needs to:
• Set the Initial Sequence Number if necessary. 
The sequence number of the firstarriving segment that has the SYN flag set is the initial sequence number. 
You’ll want to keep track of that in order to keep converting between 32-bit wrapped seqnos/acknos
and their absolute equivalents. 
(Note that the SYN flag is just one flag in the header.
The same segment could also carry data and could even have the FIN flag set.)

• Push any data, or end-of-stream marker, to the StreamReassembler. If the
FIN flag is set in a TCPSegment’s header, that means that the last byte of the payload
is the last byte of the entire stream. Remember that the StreamReassembler expects
stream indexes starting at zero; you will have to unwrap the seqnos to produce these.
*/
bool TCPReceiver::segment_received(const TCPSegment &seg) {
    
    TCPHeader head = seg.header();
    bool SYN = head.syn;
    bool eof = head.fin;
    std::string data = seg.payload().copy();
    size_t lenData = data.length();
    //cout<<"Segment: "<<head.seqno<<" "<<head.ack<<" SYN="<<SYN<<" EOF="<<eof<<endl;

    //deal with SYN
    if(haveISN && SYN) return false;// refuse other SYN if have SYN now
    if(SYN) { // ack init
        ISN = head.seqno;
        haveISN = true;
        ack = 1; //ack include SYN flag
        _reassembler.reassembleByte = 0;
    }
    else {
        if(!haveISN) return false;// before get SYN,refuse any segment
    }
    //cal the bounder
    size_t winL = WrappingInt32(wrap(ack,ISN)).raw_value();
    size_t winR = WrappingInt32(wrap(ack,ISN)).raw_value() + window_size() - 1;
    size_t dataL = head.seqno.raw_value();
    size_t dataR = head.seqno.raw_value()+lenData;
    if(lenData) dataR -= 1;

    //deal with fin
    if(head.fin && haveFIN) return false;
    //now fin and syn is valid,deal with bound
    if(lenData){
        if(dataL > winR || dataR < winL) {
            return false;
        }
    }

    // update Message
    //cout<<"data["<<dataL<<","<<dataR<<"]"<<endl;
    //cout<<"win["<<winL<<","<<winR<<"]"<<endl;
    size_t index = unwrap(head.seqno,ISN,absSeq);// the absSeq of new data
    index = index - 1;
    if(SYN) index = 0;
    /*
    cout<<"ack= "<<ack<<" "<<"dataLen= "<<lenData<<" "<<"seqno= "<<head.seqno
    <<" ISN= "<<ISN.raw_value()<<" "<<"Index= "<<index<<endl;
    */
    if(lenData){
        _reassembler.push_substring(data,index,eof);
    }
    ack = _reassembler.reassembleByte + 1;
    absSeq = max(absSeq,index);
   // cout<<"capcity= "<<_capacity<<" "<<_reassembler.unassembled_bytes()<<endl;
    
    if(head.fin) {
        if(!haveFIN){
            ack = ack + 1;// maybe ack++ after push_substring,so this code needed to be put here
            haveFIN = true;
        }
    }

    if(eof){
        _reassembler.stream_out().end_input();
    }
    return true;
}

optional<WrappingInt32> TCPReceiver::ackno() const { 
    if(!haveISN) return {};
    else return WrappingInt32(wrap(ack,ISN));
}

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

wrapping_integers.cc

#include "wrapping_integers.hh"
#include <stdlib.h>
// Dummy implementation of a 32-bit wrapping integer

// For Lab 2, please replace with a real implementation that passes the
// automated checks run by `make check_lab2`.

template <typename... Targs>
void DUMMY_CODE(Targs &&... /* unused */) {}

using namespace std;

//! 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) {
    //DUMMY_CODE(n, isn);
    uint64_t realN = n + isn.raw_value();
    realN %= 4294967296;
    WrappingInt32 ret = WrappingInt32(static_cast<uint32_t>(realN)); 
    return ret;
}

//! 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 abs(uint64_t x,uint64_t y){
    if(x > y) return x-y;
    else return y-x;
}

uint64_t unwrap(WrappingInt32 n, WrappingInt32 isn, uint64_t checkpoint) {
    uint64_t absSe = (n.raw_value() - isn.raw_value() + 4294967296) % 4294967296;
    uint64_t delta = abs(absSe,checkpoint);
    uint64_t ret = absSe;
    for(size_t i = 32;i < 64;i++){
        uint64_t tmp = absSe + (1ll<<i);
        uint64_t deltaTmp = abs(tmp,checkpoint);
        if(deltaTmp < delta){
            delta = deltaTmp;
            ret = tmp;
        }
    }
    return ret;
}

结算画面

image

posted @   liyishui  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
历史上的今天:
2023-02-28 Codeforces Beta Round #19 D. Points 线段树+set
2023-02-28 CodeForces-483D Interesting Array 线段树拆位
点击右上角即可分享
微信分享提示