120 lines
4.0 KiB
C++
120 lines
4.0 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(retx_timeout) {}
|
|
|
|
uint64_t TCPSender::bytes_in_flight() const { return _next_seqno - _unack_seqno; }
|
|
|
|
void TCPSender::fill_window() {
|
|
auto _fake_window_size = _window_size ? _window_size : 1UL;
|
|
auto occupied = _next_seqno - _unack_seqno;
|
|
auto available = _fake_window_size > occupied ? _fake_window_size - occupied : 0;
|
|
//* judge syn
|
|
auto syn = 0UL;
|
|
if (_next_seqno == 0 && available) {
|
|
syn = 1;
|
|
available -= syn;
|
|
}
|
|
//* read payload
|
|
auto payload = _stream.read(available);
|
|
available -= payload.size();
|
|
//* judge fin
|
|
auto fin = 0UL;
|
|
if (_stream.eof() && available && (_next_seqno < _stream.bytes_read() + 2)) {
|
|
fin = 1;
|
|
available -= fin;
|
|
}
|
|
//* assemble segments && split payload
|
|
size_t ptr = 0;
|
|
do {
|
|
TCPHeader hdr;
|
|
TCPSegment seg;
|
|
if (syn) {
|
|
hdr.syn = true;
|
|
syn = 0;
|
|
}
|
|
if (fin && ptr + TCPConfig::MAX_PAYLOAD_SIZE >= payload.size()) {
|
|
hdr.fin = true;
|
|
}
|
|
std::string subpayload = payload.substr(ptr, TCPConfig::MAX_PAYLOAD_SIZE);
|
|
ptr += subpayload.size();
|
|
hdr.seqno = next_seqno();
|
|
seg.header() = hdr;
|
|
seg.payload() = Buffer(std::move(subpayload));
|
|
if (seg.length_in_sequence_space()) {
|
|
_segments_out.push(seg);
|
|
_outstandings.push(seg);
|
|
_next_seqno += seg.length_in_sequence_space();
|
|
_timer.start();
|
|
}
|
|
} while (ptr < payload.size());
|
|
}
|
|
|
|
//! \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);
|
|
}
|