CS144Lab/libsponge/tcp_sender.cc
2023-02-16 16:57:39 +00:00

118 lines
4.1 KiB
C++

#include "tcp_sender.hh"
#include "tcp_config.hh"
#include <random>
// 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 <typename... Targs>
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<WrappingInt32> fixed_isn)
: _isn(fixed_isn.value_or(WrappingInt32{random_device()()}))
, _initial_retransmission_timeout{retx_timeout}
, _stream(capacity) {
_timer.reinit(_initial_retransmission_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();
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.reinit(_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.started())
return;
_timer.count_down(ms_since_last_tick);
if (!_timer.expired())
return;
_segments_out.push(_outstandings.front());
if (_window_size > 0)
_timer.consec_retrans();
_timer.reset();
}
unsigned int TCPSender::consecutive_retransmissions() const { return _timer.consec_retrans_count(); }
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);
}