#include "tcp_sender.hh" #include "tcp_config.hh" #include // Dummy implementation of a TCP sender // For Lab 3, please replace with a real implementation that passes the // automated checks run by `make check_lab3`. template void DUMMY_CODE(Targs &&.../* unused */) {} using namespace std; //! \param[in] capacity the capacity of the outgoing byte stream //! \param[in] retx_timeout the initial amount of time to wait before retransmitting the oldest outstanding segment //! \param[in] fixed_isn the Initial Sequence Number to use, if set (otherwise uses a random ISN) TCPSender::TCPSender(const size_t capacity, const uint16_t retx_timeout, const std::optional fixed_isn) : _isn(fixed_isn.value_or(WrappingInt32{random_device()()})) , _initial_retransmission_timeout{retx_timeout} , _stream(capacity) , _timer(retx_timeout) {} uint64_t TCPSender::bytes_in_flight() const { return _next_seqno - _unack_seqno; } void TCPSender::fill_window() { if (_stream.eof() && _next_seqno - _stream.bytes_read() == 2) return; TCPHeader hdr; hdr.seqno = next_seqno(); if (_next_seqno == 0) { hdr.syn = true; } auto _fake_window_size = _window_size ? _window_size : 1UL; auto occupied = _next_seqno - _unack_seqno; auto data = _stream.read(_fake_window_size - occupied); size_t sendptr = 0; std::string payload = data.substr(sendptr, TCPConfig::MAX_PAYLOAD_SIZE); if (data.size() > TCPConfig::MAX_PAYLOAD_SIZE) while (payload.size() == TCPConfig::MAX_PAYLOAD_SIZE) { TCPSegment seg; seg.header() = hdr; seg.payload() = Buffer(std::move(payload)); _segments_out.push(seg); _outstandings.push(seg); sendptr += TCPConfig::MAX_PAYLOAD_SIZE; _next_seqno += seg.length_in_sequence_space(); hdr.syn = false; hdr.seqno = next_seqno(); _timer.start(); if (sendptr >= data.size()) break; payload = data.substr(sendptr, TCPConfig::MAX_PAYLOAD_SIZE); } occupied = _next_seqno - _unack_seqno + payload.size(); if (_stream.eof() && _fake_window_size > occupied) { hdr.fin = true; } TCPSegment seg; seg.header() = hdr; seg.payload() = Buffer(std::move(payload)); if (seg.length_in_sequence_space()) { _segments_out.push(seg); _outstandings.push(seg); _next_seqno += seg.length_in_sequence_space(); _timer.start(); } } //! \param ackno The remote receiver's ackno (acknowledgment number) //! \param window_size The remote receiver's advertised window size void TCPSender::ack_received(const WrappingInt32 ackno, const uint16_t window_size) { auto abs_ackno = unwrap(ackno, _isn, _next_seqno); if (abs_ackno > _next_seqno) return; _window_size = window_size; while (!_outstandings.empty()) { // auto abs_seqno = unwrap(_outstandings.front().header().seqno, _isn, _next_seqno); if (abs_ackno >= _unack_seqno + _outstandings.front().length_in_sequence_space()) { _unack_seqno += _outstandings.front().length_in_sequence_space(); _outstandings.pop(); _timer.reset_init(_initial_retransmission_timeout); } else { break; } } if (_outstandings.empty()) { _timer.stop(); } else { _timer.start(); } } //! \param[in] ms_since_last_tick the number of milliseconds since the last call to this method void TCPSender::tick(const size_t ms_since_last_tick) { if (!_timer.is_started()) return; _timer.tick(ms_since_last_tick); if (!_timer.is_expired()) return; _segments_out.push(_outstandings.front()); if (_window_size > 0) _timer.retransmit(); _timer.reset(); _timer.start(); } unsigned int TCPSender::consecutive_retransmissions() const { return _timer.consecutive_retransmissions(); } void TCPSender::send_empty_segment() { TCPHeader hdr; hdr.seqno = wrap(_next_seqno - (_next_seqno ? 1 : 0), _isn); TCPSegment seg; seg.header() = hdr; _segments_out.push(seg); }