diff --git a/libsponge/tcp_connection.cc b/libsponge/tcp_connection.cc index 3a54672..3939333 100644 --- a/libsponge/tcp_connection.cc +++ b/libsponge/tcp_connection.cc @@ -21,35 +21,30 @@ size_t TCPConnection::unassembled_bytes() const { return _receiver.unassembled_b size_t TCPConnection::time_since_last_segment_received() const { return _time_last_segment_received; } void TCPConnection::segment_received(const TCPSegment &seg) { - if (!_active) - return; + // god knows whether it should be active or not initially _time_last_segment_received = 0; if (seg.header().rst) { - _sender.stream_in().set_error(); - _receiver.stream_out().set_error(); - _active = false; + _set_rst(); return; } - bool send_ack = seg.length_in_sequence_space(); _receiver.segment_received(seg); if (seg.header().ack && _receiver.ackno().has_value()) { // filter out acks before syn _sender.ack_received(seg.header().ackno, seg.header().win); - // _sender.fill_window(); - if (!_sender.segments_out().empty()) send_ack = false; + // _sender.fill_window(); !! This should be done in ack_received } - // std::cerr << seg.header().to_string(); - // std::cerr << _receiver.stream_out().bytes_written() << " " << _receiver.unassembled_bytes() << std::endl; - // std::cerr << seg.payload().size() << std::endl; - - if (send_ack) { - _sender.fill_window(); + // if the inbound seg has len it should be responded + // except that ack_received() has send a seg, so no more is needed + if (seg.length_in_sequence_space() && _sender.segments_out().empty()) { + _sender.fill_window(); // it is necessary to repond to SYN with a SYNACK if (_sender.segments_out().empty()) { _sender.send_empty_segment(); } } _send_all(); // TODO: Keep-alive but it seems to be use + // recver in FIN_RECV + // sender in SYN_ACKED if (_receiver.stream_out().input_ended() && !_sender.stream_in().eof()) { _linger_after_streams_finish = false; } @@ -70,30 +65,19 @@ void TCPConnection::tick(const size_t ms_since_last_tick) { _time_last_segment_received += ms_since_last_tick; _sender.tick(ms_since_last_tick); if (_sender.consecutive_retransmissions() > TCPConfig::MAX_RETX_ATTEMPTS) { - _sender.send_empty_segment(); - auto &seg_queue = _sender.segments_out(); - auto seg = seg_queue.front(); - seg_queue.pop(); - if (_receiver.ackno().has_value()) { - seg.header().ack = true; - seg.header().ackno = _receiver.ackno().value(); - } - seg.header().win = _receiver.window_size(); - seg.header().rst = true; - _segments_out.push(seg); - _sender.stream_in().set_error(); - _receiver.stream_out().set_error(); - _active = false; + _send_rst(); + _set_rst(); return; } _send_all(); - if (!_sender.bytes_in_flight() && _sender.stream_in().eof() && _receiver.stream_out().input_ended() && - !_receiver.unassembled_bytes()) { - if (!_linger_after_streams_finish) { - _active = false; - } else if (_time_last_segment_received >= 10 * _cfg.rt_timeout) { - _active = false; - } + // actually, this long if could be represented by TCP states + // for sender, it is the FIN_ACKED state + // for recver, it is the FIN_RECV state + // and plus linger + if ((!_sender.bytes_in_flight() && _sender.stream_in().eof()) && + (_receiver.stream_out().input_ended() && !_receiver.unassembled_bytes()) && + (!_linger_after_streams_finish || _time_last_segment_received >= 10 * _cfg.rt_timeout)) { + _active = false; } } @@ -112,20 +96,8 @@ TCPConnection::~TCPConnection() { try { if (active()) { cerr << "Warning: Unclean shutdown of TCPConnection\n"; - _sender.send_empty_segment(); - auto &seg_queue = _sender.segments_out(); - auto seg = seg_queue.front(); - seg_queue.pop(); - if (_receiver.ackno().has_value()) { - seg.header().ack = true; - seg.header().ackno = _receiver.ackno().value(); - } - seg.header().win = _receiver.window_size(); - seg.header().rst = true; - _segments_out.push(seg); - _sender.stream_in().set_error(); - _receiver.stream_out().set_error(); - _active = false; + _send_rst(); + _set_rst(); // Your code here: need to send a RST segment to the peer } } catch (const exception &e) { diff --git a/libsponge/tcp_connection.hh b/libsponge/tcp_connection.hh index bee8aba..1131971 100644 --- a/libsponge/tcp_connection.hh +++ b/libsponge/tcp_connection.hh @@ -5,6 +5,7 @@ #include "tcp_receiver.hh" #include "tcp_sender.hh" #include "tcp_state.hh" + #include #include @@ -22,21 +23,58 @@ class TCPConnection { //! for 10 * _cfg.rt_timeout milliseconds after both streams have ended, //! in case the remote TCPConnection doesn't know we've received its whole stream? bool _linger_after_streams_finish{true}; + + //! Private state for time_since_last_segment_received() size_t _time_last_segment_received{0}; + + //! Private state for active() bool _active{true}; + + //!\name Internal helper procedures + //!@{ + + //!\brief Send all available outbound segments void _send_all() { - auto &seg_queue = _sender.segments_out(); - while (!seg_queue.empty()) { - auto seg = seg_queue.front(); - seg_queue.pop(); - if (_receiver.ackno().has_value()) { - seg.header().ack = true; - seg.header().ackno = _receiver.ackno().value(); - } - seg.header().win = std::min(_receiver.window_size(), static_cast(std::numeric_limits::max())); - _segments_out.push(seg); - } + auto &seg_queue = _sender.segments_out(); + while (!seg_queue.empty()) { + auto seg = seg_queue.front(); + seg_queue.pop(); + if (_receiver.ackno().has_value()) { + seg.header().ack = true; + seg.header().ackno = _receiver.ackno().value(); + } + seg.header().win = + std::min(_receiver.window_size(), static_cast(std::numeric_limits::max())); + _segments_out.push(seg); + } } + + //!\brief Deprecate all other packets in the queue, and send a RST segment + void _send_rst() { + auto &seg_queue = _sender.segments_out(); + while (!seg_queue.empty()) { + seg_queue.pop(); + } + _sender.send_empty_segment(); + auto seg = seg_queue.front(); + seg_queue.pop(); + if (_receiver.ackno().has_value()) { + seg.header().ack = true; + seg.header().ackno = _receiver.ackno().value(); + } + seg.header().win = _receiver.window_size(); + seg.header().rst = true; + _segments_out.push(seg); + } + + //!\brief Set connection to Dead State + void _set_rst() { + _sender.stream_in().set_error(); + _receiver.stream_out().set_error(); + _active = false; + } + //!@} + public: //! \name "Input" interface for the writer //!@{