【CS144】Spongeの类图分析(lab0~lab4)

lab4写不下去了,感觉对代码理不清了,打算重新整理一下。
重点是五个类,(正好对应lab0~lab4)

ByteStream类


这里只展示startcode中类的模样(具体的实现可能各有不同,所以重点看函数的功能,成员变量倒不太需要分析)

ByteStream是一个字节流(An in-memory reliable byte stream)。可读可写,对外提供的接口功能如下:

  • ByteStream(const size_t capacity)
    构造函数,传入的参数是这个字节流的最大容量
  • bool input_ended()
    写端使用,当写端需要往ByteStream中写的内容写完之后,调用这个函数通知ByteStream,写端写完啦。
  • bool eof()
    读端使用,当读这个ByteStream读到最后没东西可以读了(ByteStream没东西,且写端写完啦),ByteStream.eof就会返回true
  • size_t write(const std::string &data)
    写端使用,往里面写data,返回实际写入的大小
  • std::string read(const size_t len)
    读端使用,从ByteStream中读len字节,返回实际读到的字符串
  • size_t bytes_written()
    都能使用,返回写端往ByteStream中累计写了多少字节。

StreamReassembler类


流重组器,把输入的零散的子字符串(子字符串开始编号 + 内容),组成一个连续的流(ByteStream)。(Stitching Substrings Into A Byte Stream)

  • StreamReassembler(const size_t capacity);
    构造函数,传入参数为流重组器的容量,也就是其中流ByteStream的容量
  • void push_substring(const std::string &data, const uint64_t index, const bool eof)
    写端使用,往流重组器写入子字符串(开始于index,内容为data,是否是最后一段eof)
  • ByteStream &stream_out()
    读端使用,返回组好的ByteStream
  • size_t unassembled_bytes()
    都能使用,返回还没组好的,暂存在辅助存储的数据大小,按容量的含义来说,unassembled_bytes + ByteStream.size <= capacaity
  • empty() const;
    都能使用true if no substrings are waiting to be assembled,也就是unassembled_bytes() == 0

TCPReceiver类


收到其他端发送来的TCP段后,我们需要将段中的内容放入我们的流重组器,并且计算我们的自己的ackno以及window_size,这样我们在向其发送TCP段时,就能将自己的ackno和window_size放入到TCP段首部啦。

注意看,这是我们发送给别人的TCP段,红色框框框起来filed(ackno,win)就是我们这个TCPReceiver需要计算出来的,而蓝色框框框起来的filed(seqno,payload,FIN,SYN)就是之后的TCPSender需要计算出来的。

  • TCPReceiver(const size_t capacity)
    构造函数,传入参数为capacity,也就是我们拥有的流重组器的数据容量(syn和fin空包虽然占seqno,但不占capacity)
  • WrappingInt32 ackno()
    发送TCP段时使用,填入到TCP段首部的ackno中,意味着我们已经收到了ackno编号之前的所有数据,期望收到ackno编号以及其之后的数据
  • size_t window_size()
    发送TCP段时使用,填入到TCP段首部的win中,意味着我们流重组器还能接受多少数据,也就是下图的红色部分

    其中的,first_unassembled + isn + (syn和fin占据的seqno)就是我们的ackno
  • void segment_received(const TCPSegment &seg)
    接受TCP段时使用,填入接收到的TCP段,我们会将TCP段的payload放入我们的流重组器,并且重新设置我们的window_size和ackno

TCPReceiver有四个状态(注意,这是我们自己写的类的状态,不是TCP连接状态图中的状态(但也有关系)!)

  • LISTEN状态
    监听状态,说明此时还没有收到其他端的TCP段,我们也从未将数据放入流重组器中过,所以我们还没有产生过ackno,故判断条件是ackno().has_value == false
  • SYN_RECV状态
    流已经开启,说明此时已经收到了SYN段,并且流还没有结束(流结束意味着我们收到的FIN段,且都重组完了,那时就会调用ByteStream的end_input,说明ByteStream的写端写完了)
    所以判断条件是ackno().has_value == true并且stream_out().input_ended() == false
  • FIN_RECV状态
    表示流结束啦,即我们收到的FIN段,并且FIN段已经顺利重组到流重组器中了(即流重组器中不存在暂存的尚未存储的子字符串了),这时会调用ByteStream(也就是stream_out())的end_input,说明ByteStream的写端写完啦
  • ERROR状态
    表示流错误,判断条件stream_out().error() == true

TCPSender类


Sender类的作用是往对端发送TCP段,需要往其首部中填入seqno,FIN,SYN,payload这些filed。当然,我们是往别人端里发送TCP段,就需要知道别人的ackno和window_size啦,这也就是别人的TCPReceiver所维护的信息。

  • TCPSender(size_t capacity, uint16_t retx_timeout, WrappingInt32 fixed_isn)
    构造函数,传入的是容量(是其成员变量ByteStream的容量,其他调用这个ByteStream写数据,TCPSender读ByteStream中数据发送出去),retx_timeout是初始超时时间,fixed_isn则是我方发送syn包时携带的随机seqno

  • void ack_received(const WrappingInt32 ackno, const uint16_t window_size)
    接受TCP段时调用,我们收到其他端发来的TCP段后,可以从TCP段的首部知道对方的ackno和window_size,那么我们就能根据这些信息发送对方所需要的内容。

  • void send_empty_segment()
    发个空TCP段,seqno是我们下次需要发的编号(_next_seqno),不携带内容,外部会再给这个段附带上我们的window_size和ackno,主要用于创造空的ack段。

  • void fill_window()
    我们接受TCP段时会调用ack_received,而ack_received收到对方的window_size,ackno等数据后,就知道对方需要啥的,这时候调用fill_window,就能将对方能接收的,所需要的段发送出去。

  • void tick(const size_t ms_since_last_tick)
    外部调用,我们发送的段可能不被接受呀,超时后就需要重传已经发送的段!注意,syn段,fin段啥的需要重传,而空段(empty_segment)不需要重传!

  • size_t bytes_in_flight()
    外部调用,表示我们有多少字节(其实是seqno,syn段,fin段虽然不携带内容,但占seqno哦)发送出来还没被ack

  • uint64_t next_seqno_absolute()
    外部调用,返回我们下次需要发送的seqno(从0开始,0是syn段占用的seqno)

cool,sender也有自己的状态图!

  • CLOSED状态
    表示waiting for stream to begin (no SYN sent),意思是我们连SYN段都没发,判断条件是next_seqno_absolute() == 0,注意看,next_seqno为0,表示我们下一个需要发送的seqno是0,而0往往是SYN段所占用了,所以我们连SYN段都没发。
  • SYN_SENT状态
    表示stream started but nothing acknowledged,意思是我们发了第一个TCP段,但尚未收到对方的ack段。判断条件是next_seqno_absolute() > 0,意味着我们已经发了,而next_seqno_absolute() == bytes_in_flight(),意味着我们发的还是空中(尚未ack),从这里也可以看出,bytes_in_flight返回的值是尚未确定的seqno数目,而不是字节数(syn段这种就是只占seqno,不占bytes)
  • SYN_ACKED状态
    表示我们以及收到了第一个ack包,stream建成啦,且我们还能从ByteStream中读出数据发送出去。判断条件是next_seqno_absolute() > bytes_in_flight() && not stream_in().eof()
  • SYN_ACKED状态
    表示stream has reached EOF, but FIN flag hasn't been sent yet,意味着我们的ByteStream读完啦,但还没给对方发送FIN段。判断条件是stream_in().eof()并且next_seqno_absolute() < stream_in().bytes_written() + 2,+2是因为syn和fin都占用seqno
  • FIN_SENT状态
    表示stream finished (FIN sent) but not fully acknowledged,意味着我们发了FIN段,还没收到FIN段的ack呢。
  • FIN_ACKED状态
    发送了FIN段,且收到的FIN段的ack

TCP State


TCPState::TCPState(const TCPState::State state) {
    switch (state) {
        case TCPState::State::LISTEN:
            _receiver = TCPReceiverStateSummary::LISTEN;
            _sender = TCPSenderStateSummary::CLOSED;
            break;
        case TCPState::State::SYN_RCVD:
            _receiver = TCPReceiverStateSummary::SYN_RECV;
            _sender = TCPSenderStateSummary::SYN_SENT;
            break;
        case TCPState::State::SYN_SENT:
            _receiver = TCPReceiverStateSummary::LISTEN;
            _sender = TCPSenderStateSummary::SYN_SENT;
            break;
        case TCPState::State::ESTABLISHED:
            _receiver = TCPReceiverStateSummary::SYN_RECV;
            _sender = TCPSenderStateSummary::SYN_ACKED;
            break;
        case TCPState::State::CLOSE_WAIT:
            _receiver = TCPReceiverStateSummary::FIN_RECV;
            _sender = TCPSenderStateSummary::SYN_ACKED;
            _linger_after_streams_finish = false;
            break;
        case TCPState::State::LAST_ACK:
            _receiver = TCPReceiverStateSummary::FIN_RECV;
            _sender = TCPSenderStateSummary::FIN_SENT;
            _linger_after_streams_finish = false;
            break;
        case TCPState::State::CLOSING:
            _receiver = TCPReceiverStateSummary::FIN_RECV;
            _sender = TCPSenderStateSummary::FIN_SENT;
            break;
        case TCPState::State::FIN_WAIT_1:
            _receiver = TCPReceiverStateSummary::SYN_RECV;
            _sender = TCPSenderStateSummary::FIN_SENT;
            break;
        case TCPState::State::FIN_WAIT_2:
            _receiver = TCPReceiverStateSummary::SYN_RECV;
            _sender = TCPSenderStateSummary::FIN_ACKED;
            break;
        case TCPState::State::TIME_WAIT:
            _receiver = TCPReceiverStateSummary::FIN_RECV;
            _sender = TCPSenderStateSummary::FIN_ACKED;
            break;
        case TCPState::State::RESET:
            _receiver = TCPReceiverStateSummary::ERROR;
            _sender = TCPSenderStateSummary::ERROR;
            _linger_after_streams_finish = false;
            _active = false;
            break;
        case TCPState::State::CLOSED:
            _receiver = TCPReceiverStateSummary::FIN_RECV;
            _sender = TCPSenderStateSummary::FIN_ACKED;
            _linger_after_streams_finish = false;
            _active = false;
            break;
    }
}

注意看,这段代码代表了TCPSender、TCPReceiver的状态和TCP连接的状态对应关系。

posted @ 2022-10-06 09:36  PinganT  阅读(168)  评论(0)    收藏  举报