当前位置: 首页 > Linux

CS144LabAssignments-HandwrittenTCP-LAB4

时间:2023-04-06 02:46:04 Linux

CS144:IntroductiontoComputerNetworking,Fall2020https://cs144.github.io/MyRepohttps://github.com/wine99/cs1...这部分实现了TCPConnection类,实例化此类将充当完整TCP连接中的对等方(可以充当任何一方,服务器或客户端)。前面两个实验实现的TCPSender和TCPReceiver不能作为一个独立的Server或者Client使用。这两个类的实例作为TCPConnection实例的内部成员,如下图所示。Sender和Receiver的作用Notify_receiver在收到一个报文段时:根据报文段的seqno、SYN、FIN和payload,以及当前状态更新ackno;收集数据通知_sender:根据报文段的ackno和当前状态,更新next_seqno;updatewindow_size发送报文段时,\_sender负责填充payload、seqno、SYN、FIN。注意可能既没有payload也没有S,F标识(空段),这和ack_received()的逻辑不同\_receiver负责填充ackno,windowsizeFSM结合TCPSender和TCPReceiver的状态转换图在Lab2和Lab3的讲义中,tcp_state.cc中TCPConnection的状态与sender和receiver状态的对应关系,以及下图TCPConnection的状态转换,了解整个TCP连接。在Edgecase的实现过程中,需要额外注意TCPSender和TCPConnection在接收??报文时的逻辑区别。这些细节来自于Lab2中的接收者只关心接收到的数据和与数据相关的标识;Lab3中的sender只关心收到的ackno和win,不处理也不知道收到的数据等信息。在\_stream_in()中没有Data可能不会采取任何操作(我的Lab3实现是这样的),而在Lab4中可能需要发送一个空的ACK段。连接建立和释放过程中的各种特殊情况,发送SYN后立即收到RST发送SYN后立即收到FINSimultaneousopenSimultaneousshutdown...实验给出的testsuite很全,涵盖了各种特殊情况。SimultaneousopenandSimultaneousshutdown的情况如下图所示。根据讲义,如果你的Lab2和Lab3实现非常健壮,那么Lab4中的大部分工作就是连接前两个类的接口,但也有可能你需要修改前两个实验的实现.下图来源:TCPStateTransitions实现了我实验4的功能框架,参考了这篇博客,但是实现方式不一样。我在网上浏览过的几个实现改变了Lab2和Lab3的函数签名,使得Lab2和Lab3的实现不太干净。我最终的实现没有侵入Lab3和Lab2的代码,详细的逻辑都在TCPConnection类中完成。注意如果tests文件夹下的测试全部通过但是txrx.sh中的测试失败,失败的原因是结果的hash值不一样,把你添加的打印语句全部去掉,并且然后测试。刚开始做实验四的时候想放弃,但是最后用的时间比实验三少(实验三零碎的大概用了六天,实验四密集的大概用了两天半)。当我通过所有测试时,我仍然感到有些困惑。我是怎么通过的?我真的处理了所有的细节吗?第一次体会到,在一个复杂的项目中,一个完整的测试远比一个“自信”的实现代码靠谱得多,不得不感受到课程的高质量和讲师、助教的辛勤付出。代码添加的成员变量classTCPConnection{private:size_t_time_since_last_segment_received{0};布尔_active{true};voidsend_sender_segments();voidclean_shutdown();voidunclean_shutdown();现实代码#include"tcp_connection.hh"#includeusingnamespacestd;size_tTCPConnection::remaining_outbound_capacity()const{return_sender.stream_in().remaining_capacity();}size_tTCPConnection::bytes_in_flight()const{return_sender.bytes_in_flight();}size_tTCPConnection::unassembled_bytes()const{return_receiver.unassembled_bytes();}size_tTCPConnection::time_since_last_segment_received()const{return_time_since_last_segment_received;}boolTCPConnection::active()const{return_active;}voidTCPConnection::segment_received(constTCPSegment&seg){if(!_active)return;_time_since_last_segment_received=0;//状态:关闭if(!_receiver.ackno().has_value()&&_sender.next_seqno_absolute()==0){如果(!seg.header().syn)返回;_receiver.segment_received(段);连接();返回;}//状态:syn发送if(_sender.next_seqno_absolute()>0&&_sender.bytes_in_flight()==_sender.next_seqno_absolute()&&!_receiver.ackno().has_value()){if(seg.payload().大小())返回;if(!seg.header().ack){if(seg.header().syn){//同时打开_receiver.segment_received(seg);_sender.send_empty_segment();}返回;}if(seg.header().rst){_receiver.stream_out().set_error();_sender.stream_in().set_error();_active=假;返回;}}_receiver.segment_received(seg);_sender.ack_received(seg.header().ackno,seg.header().win);//Lab3行为:fill_window()会直接返回而不用发送ng任何段。//参见tcp_sender.cc第42行如果(seg.header().rst){_sender.send_empty_segment();不干净的关机();返回;}send_sender_segments();}size_tTCPConnection::write(conststring&data){if(!data.size())return0;size_twrite_size=_sender.stream_in().write(data);_sender.fill_window();send_sender_segments();返回写入大小;}//!\param[in]ms_since_last_tick自上次调用此方法以来的毫秒数voidTCPConnection::tick(constsize_tms_since_last_tick){if(!_active)return;_time_since_last_segment_received+=ms_since_last_tick;_sender.tick(ms_since_last_tick);如果(_sender.consecutive_retransmissions()>TCPConfig::MAX_RETX_ATTEMPTS)unclean_shutdown();send_sender_segments();}voidTCPConnection::end_input_stream(){_sender.stream_in().end_input();_sender.fill_window();send_sender_segments();}voidTCPConnection::connect(){_sender.fill_window();send_sender_segments();}TCPConnection::~TCPConnection(){try{if(active()){cerr<<"警告:TCPConnection不干净关闭\n";_sender.send_empty_segment();不干净的关机();}}catch(constexception&e){std::cerr<<"ExceptiondestructingTCPFSM:"<=10*_cfg.rt_timeout){_active=false;}}}}性能优化分析由于没有做profiling,所以性能分析的工作照搬了上述博客的作业。修改sponge/etc/cflags.cmake中的编译参数,将-g改为-Og-pg,使生成的程序有解析器可用的链接信息。make-j8./apps/tcp_benchmarkgprof./apps/tcp_benchmark>prof.txt如讲义中所述,您很可能需要修改ByteStream或StreamReassembler。调优的方法是利用buffer.h中提供的BufferList。实际上,测试代码中使用了BufferList。简而言之就是一个deque\,整个实现和测试代码中都广泛使用了Buffer。例如,payload()是一个Buffer实例。改变Lab0原来的std::list_stream{}中ByteStream类中的字节流容器;到BufferList_stream{};。byte_stream.cc修改的函数:size_tByteStream::write(conststring&data){size_twrite_count=data.size();如果(write_count>_capacity-_buffer_size)write_count=_capacity-_buffer_size;_stream.append(BufferList(move(string().assign(data.begin(),data.begin()+write_count))));_buffer_size+=write_count;_bytes_written+=写入计数;返回写计数;}//!\param[in]len字节将从缓冲区字符串的输出端复制ByteStream::peek_output(constsize_tlen)const{constsize_tpeek_length=len>_buffer_size_buffer_size:长度;字符串str=_stream.concatenate();returnstring().assign(str.begin(),str.begin()+peek_length);}//!\param[in]len个字节将从buffervoidByteStream::pop_output(constsize_tlen){size_tpop_length=len>_buffer_size?_buffer_size:长度;_stream.remove_prefix(pop_length);_bytes_read+=pop_length;_buffer_size-=pop_length;}修改后的benchmarkwebget直接按照讲义中的步骤,将Linux自带的TCPSocket换成我们自己的实现voidget_URL(conststring&host,conststring&path){CS144TCPSocketsock1{};袜子1。连接(地址(主机,“http”));sock1.write("GET"+path+"HTTP/1.1\r\n"+"Host:"+host+"\r\n"+"Connection:close\r\n\r\n");while(!sock1.eof()){cout<