//! outbound queue of segments that the TCPConnection wants sent std::queue<TCPSegment> _segments_out{};
//! Should the TCPConnection stay active (and keep ACKing) //! 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? // linger 确保最后对对方的 FIN 的 ACK 被对方接收,否则对方可能会超时并重传FIN // 防止旧的重复报文段干扰新连接 bool _linger_after_streams_finish{true};
voidsend_RST(); boolreal_send(); voidset_ack_and_windowsize(TCPSegment& segment); // prereqs1 : The inbound stream has been fully assembled and has ended. boolcheck_inbound_ended(); // prereqs2 : The outbound stream has been ended by the local application and fully sent (including // the fact that it ended, i.e. a segment with fin ) to the remote peer. // prereqs3 : The outbound stream has been fully acknowledged by the remote peer. boolcheck_outbound_ended();
public: //! \name "Input" interface for the writer //!@{
//! \brief Initiate a connection by sending a SYN segment voidconnect();
//! \brief Write data to the outbound byte stream, and send it over TCP if possible //! \returns the number of bytes from `data` that were actually written. size_twrite(const std::string &data);
//! \returns the number of `bytes` that can be written right now. size_tremaining_outbound_capacity()const;
//! \brief Shut down the outbound byte stream (still allows reading incoming data) voidend_input_stream(); //!@}
//! \name "Output" interface for the reader //!@{
//! \brief The inbound byte stream received from the peer ByteStream &inbound_stream(){ return _receiver.stream_out(); } //!@}
//! \name Accessors used for testing
//!@{ //! \brief number of bytes sent and not yet acknowledged, counting SYN/FIN each as one byte size_tbytes_in_flight()const; //! \brief number of bytes not yet reassembled size_tunassembled_bytes()const; //! \brief Number of milliseconds since the last segment was received size_ttime_since_last_segment_received()const; //!< \brief summarize the state of the sender, receiver, and the connection TCPState state()const{ return {_sender, _receiver, active(), _linger_after_streams_finish}; }; //!@}
//! \name Methods for the owner or operating system to call //!@{
//! Called when a new segment has been received from the network voidsegment_received(const TCPSegment &seg);
//! Called periodically when time elapses voidtick(constsize_t ms_since_last_tick);
//! \brief TCPSegments that the TCPConnection has enqueued for transmission. //! \note The owner or operating system will dequeue these and //! put each one into the payload of a lower-layer datagram (usually Internet datagrams (IP), //! but could also be user datagrams (UDP) or any other kind). std::queue<TCPSegment> &segments_out(){ return _segments_out; }
//! \brief Is the connection still alive in any way? //! \returns `true` if either stream is still running or if the TCPConnection is lingering //! after both streams have finished (e.g. to ACK retransmissions from the peer) boolactive()const; //!@}
//! Construct a new connection from a configuration explicitTCPConnection(const TCPConfig &cfg) : _cfg{cfg} {}
//! \name construction and destruction //! moving is allowed; copying is disallowed; default construction not possible
//!@{ ~TCPConnection(); //!< destructor sends a RST if the connection is still open TCPConnection() = delete; TCPConnection(TCPConnection &&other) = default; TCPConnection &operator=(TCPConnection &&other) = default; TCPConnection(const TCPConnection &other) = delete; TCPConnection &operator=(const TCPConnection &other) = delete; //!@} };
// prereqs1 : The inbound stream has been fully assembled and has ended. boolTCPConnection::check_inbound_ended(){ return _receiver.unassembled_bytes() == 0 && _receiver.stream_out().input_ended(); } // prereqs2 : The outbound stream has been ended by the local application and fully sent (including // the fact that it ended, i.e. a segment with fin ) to the remote peer. // prereqs3 : The outbound stream has been fully acknowledged by the remote peer. boolTCPConnection::check_outbound_ended(){ return _sender.stream_in().eof() // +2 include SYN and FIN && _sender.next_seqno_absolute() == _sender.stream_in().bytes_written() + 2 && _sender.bytes_in_flight() == 0; }
//! \param[in] ms_since_last_tick number of milliseconds since the last call to this method voidTCPConnection::tick(constsize_t ms_since_last_tick){ _time_since_last_segment_received_counter += ms_since_last_tick; // tick the sender _sender.tick(ms_since_last_tick); // 需要重传 if(_sender.segments_out().size() > 0) { TCPSegment retx_seg = _sender.segments_out().front(); _sender.segments_out().pop(); set_ack_and_windowsize(retx_seg); // 重传次数超过限制 // 中断连接 if(_sender.consecutive_retransmissions() > _cfg.MAX_RETX_ATTEMPTS) { _sender.stream_in().set_error(); _receiver.stream_out().set_error(); retx_seg.header().rst = true; _active = false; } _segments_out.push(retx_seg); } // check if need to linger if(check_inbound_ended() && !_sender.stream_in().eof()) { _linger_after_streams_finish = false; } // check if done if(check_inbound_ended() && check_outbound_ended()) { if(!_linger_after_streams_finish) { _active = false; } elseif(_time_since_last_segment_received_counter >= 10*_cfg.rt_timeout) { // linger _active = false; } } }
TCPConnection::~TCPConnection() { try { if (active()) { cerr << "Warning: Unclean shutdown of TCPConnection\n"; // Your code here: need to send a RST segment to the peer _sender.stream_in().set_error(); _receiver.stream_out().set_error(); send_RST(); _active = false; } } catch (const exception &e) { std::cerr << "Exception destructing TCP FSM: " << e.what() << std::endl; } }