煤矸石空心砖

联系我们Contact

企业名称:桐城市南口新型建材有限公司

联系人:崔经理

电话:0556-6568069

手机:18156911555

邮箱:303927413@qq.com

地址:桐城市龙腾街道高桥村

网址:   www.nkxxjc.com 



您的当前位置: 首 页 > CS144Lab0翻译Doraemonzzz > linux

linux

CS 144: Introduction to Computer Networking, Fall 2020https://cs144.github.io/

My Repohttps://github.com/wine99/cs1...

任务

本节实现 TCPConnection 类,实例化这个类将作为一个完整的 TCP 连接中的一个 peer(可以充当任意一方,Server 或 Client)。前面两个实验分别实现的 TCPSender 和 TCPReceiver 并不能作为一个独立的 Server 或 Client,这两个类的实例是用作 TCPConnection 实例的内部成员,即下图。

ygfFsI.png

Sender 和 Receiver 的作用

收到报文段时

通知 _receiver:根据报文段的 seqno、SYN、FIN 和 payload,以及当前状态,更新 ackno;收集数据通知 \_sender:根据报文段的 ackno 以及当前状态,更新 next_seqno;更新 window_size

发送报文段时

\_sender 负责填充 payload、seqno、SYN、FIN,注意有可能既没有 payload 也没有 S、F 标识(empty segment),这和 Lab3 实现的 \_sender 的 ack_received() 逻辑不同\_receiver 负责填充 ackno、window sizeFSM

结合 Lab2、Lab3 讲义中的 TCPSender 和 TCPReceiver 的状态转换图,tcp_state.cc 中 TCPConnection 的各状态与 sender、receiver 状态的对应关系,以及下面的 TCPConnection 的状态转换图,理解整个 TCP 连接。

yg5iQJ.jpg

Edge case

在实现过程中,需要额外关注收到报文段时 TCPSender 和 TCPConnection 的逻辑的不同之处。这些细节来源于

Lab2 中的 receiver 只关心收到数据和数据有关的标识;Lab3 中 sender 只关心收到的 ackno 和 win,不处理也不知道收到的数据和其他信息,在 \_stream_in() 没有数据时可能不会做任何动作(我的 Lab3 实现是这样的),而在 Lab4 中可能还需要发一个空的 ACK 报文段

连接建立和释放过程中的各种特殊情况

发完 SYN 后马上收到 RST发完 SYN 后马上收到 FINSimultaneous openSimultaneous shutdown...

实验给出的测试套非常完备,覆盖了各种特殊情况,Simultaneous open 和 Simultaneous shutdown 的情况见下图。按照讲义所说,如果你的 Lab2 和 Lab3 实现非常 robust,Lab4 的大部分工作是 wire up 前面两个类的接口,但也有可能你需要修改前两个实验的实现。

下图出处:TCP State Transitions

yg7ih6.jpgyg7A1O.jpg

实现

我的实验四的函数框架参考了 这篇博客,但实现不同。我在网上浏览过的几个实现,均改动了 Lab2、Lab3 的函数签名,让 Lab2、Lab3 的实现变得不太干净。我的最终实现没有入侵 Lab3 和 Lab2 的代码,细节逻辑全部在 TCPConnection 类中完成。

注意如果 tests 文件夹中的测试全部通过但是 txrx.sh 中的测试不通过,并且不通过的原因是结果的哈希值不同,去掉所有的自己添加的打印语句,再进行测试。

实验四刚开始时一度想要放弃,但最终花费的时间居然比实验三要少(实验三零零碎碎花了六天左右,实验四大概花费了集中的两天半时间)。通过全部测试的时候,还感觉有点懵逼,怎么就通过了,我真的把细节都处理完了?第一次意识到,复杂的项目中,完备的测试比“充满自信”的实现代码可靠多了,也不得不感慨课程质量之高以及讲师和助教付出的心血。

y2kzRO.png

代码添加的成员变量class TCPConnection { private: size_t _time_since_last_segment_received{0}; bool _active{true}; void send_sender_segments(); void clean_shutdown(); void unclean_shutdown();实现代码#include "tcp_connection.hh" #include using namespace std; size_t TCPConnection::remaining_outbound_capacity() const { return _sender.stream_in().remaining_capacity(); } size_t TCPConnection::bytes_in_flight() const { return _sender.bytes_in_flight(); } size_t TCPConnection::unassembled_bytes() const { return _receiver.unassembled_bytes(); } size_t TCPConnection::time_since_last_segment_received() const { return _time_since_last_segment_received; } bool TCPConnection::active() const { return _active; } void TCPConnection::segment_received(const TCPSegment &seg) { if (!_active) return; _time_since_last_segment_received = 0; // State: closed if (!_receiver.ackno().has_value() && _sender.next_seqno_absolute() == 0) { if (!seg.header().syn) return; _receiver.segment_received(seg); connect(); return; } // State: syn sent if (_sender.next_seqno_absolute() > 0 && _sender.bytes_in_flight() == _sender.next_seqno_absolute() && !_receiver.ackno().has_value()) { if (seg.payload().size()) return; if (!seg.header().ack) { if (seg.header().syn) { // simultaneous open _receiver.segment_received(seg); _sender.send_empty_segment(); } return; } if (seg.header().rst) { _receiver.stream_out().set_error(); _sender.stream_in().set_error(); _active = false; return; } } _receiver.segment_received(seg); _sender.ack_received(seg.header().ackno, seg.header().win); // Lab3 behavior: fill_window() will directly return without sending any segment. // See tcp_sender.cc line 42 if (_sender.stream_in().buffer_empty() && seg.length_in_sequence_space()) _sender.send_empty_segment(); if (seg.header().rst) { _sender.send_empty_segment(); unclean_shutdown(); return; } send_sender_segments(); } size_t TCPConnection::write(const string &data) { if (!data.size()) return 0; size_t write_size = _sender.stream_in().write(data); _sender.fill_window(); send_sender_segments(); return write_size; } //! \param[in] ms_since_last_tick number of milliseconds since the last call to this method void TCPConnection::tick(const size_t ms_since_last_tick) { if (!_active) return; _time_since_last_segment_received += ms_since_last_tick; _sender.tick(ms_since_last_tick); if (_sender.consecutive_retransmissions() > TCPConfig::MAX_RETX_ATTEMPTS) unclean_shutdown(); send_sender_segments(); } void TCPConnection::end_input_stream() { _sender.stream_in().end_input(); _sender.fill_window(); send_sender_segments(); } void TCPConnection::connect() { _sender.fill_window(); send_sender_segments(); } TCPConnection::~TCPConnection() { try { if (active()) { cerr _buffer_size ? _buffer_size : len; string str = _stream.concatenate(); return string().assign(str.begin(), str.begin() + peek_length); } //! \param[in] len bytes will be removed from the output side of the buffer void ByteStream::pop_output(const size_t len) { size_t pop_length = len > _buffer_size ? _buffer_size : len; _stream.remove_prefix(pop_length); _bytes_read += pop_length; _buffer_size -= pop_length; }改动后的 benchmark

y2KfoQ.png

webget revisited

直接按照讲义中的步骤,把 Linux 自带的 TCPSocket,换成我们自己的实现。

void get_URL(const string &host, const string &path) { CS144TCPSocket sock1{}; sock1.connect(Address(host, "http")); sock1.write("GET " + path + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n"); while (!sock1.eof()) { cout

本文网址:

关键词:CS144Lab0翻译Doraemonzzz

相关新闻: