【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就会返回truesize_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()
读端使用,返回组好的ByteStreamsize_t unassembled_bytes()
都能使用,返回还没组好的,暂存在辅助存储的数据大小,按容量的含义来说,unassembled_bytes + ByteStream.size <= capacaityempty() const;
都能使用,trueif 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)就是我们的acknovoid segment_received(const TCPSegment &seg)
接受TCP段时使用,填入接收到的TCP段,我们会将TCP段的payload放入我们的流重组器,并且重新设置我们的window_size和ackno
TCPReceiver有四个状态(注意,这是我们自己写的类的状态,不是TCP连接状态图中的状态(但也有关系)!)

LISTEN状态
监听状态,说明此时还没有收到其他端的TCP段,我们也从未将数据放入流重组器中过,所以我们还没有产生过ackno,故判断条件是ackno().has_value == falseSYN_RECV状态
流已经开启,说明此时已经收到了SYN段,并且流还没有结束(流结束意味着我们收到的FIN段,且都重组完了,那时就会调用ByteStream的end_input,说明ByteStream的写端写完了)
所以判断条件是ackno().has_value == true并且stream_out().input_ended() == falseFIN_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都占用seqnoFIN_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连接的状态对应关系。

浙公网安备 33010602011771号