#ifndef SPONGE_LIBSPONGE_TCP_SENDER_HH #define SPONGE_LIBSPONGE_TCP_SENDER_HH #include "byte_stream.hh" #include "tcp_config.hh" #include "tcp_segment.hh" #include "wrapping_integers.hh" #include #include //! \brief Timer helper class suggested in the handout //! God knows what this class does class TCPTimer { private: size_t RTO; size_t _cr_count{0}; size_t _ti_count; bool _is_started{false}; public: TCPTimer(uint32_t initial_retransmission_timeout) : RTO(initial_retransmission_timeout), _ti_count(initial_retransmission_timeout) {} void reset_init(uint32_t initial_retransmission_timeout) { RTO = initial_retransmission_timeout; _ti_count = RTO; _cr_count = 0; _is_started = false; } void reset() { _ti_count = RTO; _is_started = false; } void start() { _is_started = true; } void stop() { _is_started = false; } void tick(size_t milli_sec) { if (_ti_count >= milli_sec) _ti_count -= milli_sec; else _ti_count = 0; } void retransmit() { _cr_count++; RTO <<= 1; } bool is_started() const { return _is_started; } bool is_expired() const { return _ti_count == 0; } size_t consecutive_retransmissions() const { return _cr_count; } }; //! \brief The "sender" part of a TCP implementation. //! Accepts a ByteStream, divides it up into segments and sends the //! segments, keeps track of which segments are still in-flight, //! maintains the Retransmission Timer, and retransmits in-flight //! segments if the retransmission timer expires. class TCPSender { private: //! our initial sequence number, the number for our SYN. WrappingInt32 _isn; //! outbound queue of segments that the TCPSender wants sent std::queue _segments_out{}; //! retransmission timer for the connection unsigned int _initial_retransmission_timeout; //! outgoing stream of bytes that have not yet been sent ByteStream _stream; //! the (absolute) sequence number for the next byte to be sent uint64_t _next_seqno{0}; uint64_t _unack_seqno{0}; //! current window size, default (and at least) should be 1 byte according to the handout uint16_t _window_size{1}; std::queue _outstandings{}; TCPTimer _timer; public: //! Initialize a TCPSender TCPSender(const size_t capacity = TCPConfig::DEFAULT_CAPACITY, const uint16_t retx_timeout = TCPConfig::TIMEOUT_DFLT, const std::optional fixed_isn = {}); //! \name "Input" interface for the writer //!@{ ByteStream &stream_in() { return _stream; } const ByteStream &stream_in() const { return _stream; } //!@} //! \name Methods that can cause the TCPSender to send a segment //!@{ //! \brief A new acknowledgment was received void ack_received(const WrappingInt32 ackno, const uint16_t window_size); //! \brief Generate an empty-payload segment (useful for creating empty ACK segments) void send_empty_segment(); //! \brief create and send segments to fill as much of the window as possible void fill_window(); //! \brief Notifies the TCPSender of the passage of time void tick(const size_t ms_since_last_tick); //!@} //! \name Accessors //!@{ //! \brief How many sequence numbers are occupied by segments sent but not yet acknowledged? //! \note count is in "sequence space," i.e. SYN and FIN each count for one byte //! (see TCPSegment::length_in_sequence_space()) size_t bytes_in_flight() const; //! \brief Number of consecutive retransmissions that have occurred in a row unsigned int consecutive_retransmissions() const; //! \brief TCPSegments that the TCPSender has enqueued for transmission. //! \note These must be dequeued and sent by the TCPConnection, //! which will need to fill in the fields that are set by the TCPReceiver //! (ackno and window size) before sending. std::queue &segments_out() { return _segments_out; } //!@} //! \name What is the next sequence number? (used for testing) //!@{ //! \brief absolute seqno for the next byte to be sent uint64_t next_seqno_absolute() const { return _next_seqno; } //! \brief relative seqno for the next byte to be sent WrappingInt32 next_seqno() const { return wrap(_next_seqno, _isn); } //!@} }; #endif // SPONGE_LIBSPONGE_TCP_SENDER_HH