#include "tcp_connection.hh" #include // Dummy implementation of a TCP connection // For Lab 4, please replace with a real implementation that passes the // automated checks run by `make check`. template void DUMMY_CODE(Targs &&.../* unused */) {} using namespace std; size_t TCPConnection::remaining_outbound_capacity() const { return _sender.stream_in().remaining_capacity(); } size_t TCPConnection::bytes_in_flight() const { return _sender.bytes_in_flight(); } size_t TCPConnection::unassembled_bytes() const { return _receiver.unassembled_bytes(); } size_t TCPConnection::time_since_last_segment_received() const { return _time_last_segment_received; } void TCPConnection::segment_received(const TCPSegment &seg) { if (!_active) return; _time_last_segment_received = 0; if (seg.header().rst) { _sender.stream_in().set_error(); _receiver.stream_out().set_error(); _active = false; return; } bool send_ack = seg.length_in_sequence_space(); _receiver.segment_received(seg); if (seg.header().ack && _receiver.ackno().has_value()) { // filter out acks before syn _sender.ack_received(seg.header().ackno, seg.header().win); // _sender.fill_window(); if (!_sender.segments_out().empty()) send_ack = false; } // std::cerr << seg.header().to_string(); // std::cerr << _receiver.stream_out().bytes_written() << " " << _receiver.unassembled_bytes() << std::endl; // std::cerr << seg.payload().size() << std::endl; if (send_ack) { _sender.fill_window(); if (_sender.segments_out().empty()) { _sender.send_empty_segment(); } } _send_all(); // TODO: Keep-alive but it seems to be use if (_receiver.stream_out().input_ended() && !_sender.stream_in().eof()) { _linger_after_streams_finish = false; } } bool TCPConnection::active() const { return _active; } size_t TCPConnection::write(const string &data) { auto &stream_in = _sender.stream_in(); auto wr_sz = stream_in.write(data); _sender.fill_window(); _send_all(); return wr_sz; } //! \param[in] ms_since_last_tick number of milliseconds since the last call to this method void TCPConnection::tick(const size_t ms_since_last_tick) { _time_last_segment_received += ms_since_last_tick; _sender.tick(ms_since_last_tick); if (_sender.consecutive_retransmissions() > TCPConfig::MAX_RETX_ATTEMPTS) { _sender.send_empty_segment(); auto &seg_queue = _sender.segments_out(); auto seg = seg_queue.front(); seg_queue.pop(); if (_receiver.ackno().has_value()) { seg.header().ack = true; seg.header().ackno = _receiver.ackno().value(); } seg.header().win = _receiver.window_size(); seg.header().rst = true; _segments_out.push(seg); _sender.stream_in().set_error(); _receiver.stream_out().set_error(); _active = false; return; } _send_all(); if (!_sender.bytes_in_flight() && _sender.stream_in().eof() && _receiver.stream_out().input_ended() && !_receiver.unassembled_bytes()) { if (!_linger_after_streams_finish) { _active = false; } else if (_time_last_segment_received >= 10 * _cfg.rt_timeout) { _active = false; } } } void TCPConnection::end_input_stream() { _sender.stream_in().end_input(); _sender.fill_window(); _send_all(); } void TCPConnection::connect() { _sender.fill_window(); _send_all(); } TCPConnection::~TCPConnection() { try { if (active()) { cerr << "Warning: Unclean shutdown of TCPConnection\n"; _sender.send_empty_segment(); auto &seg_queue = _sender.segments_out(); auto seg = seg_queue.front(); seg_queue.pop(); if (_receiver.ackno().has_value()) { seg.header().ack = true; seg.header().ackno = _receiver.ackno().value(); } seg.header().win = _receiver.window_size(); seg.header().rst = true; _segments_out.push(seg); _sender.stream_in().set_error(); _receiver.stream_out().set_error(); _active = false; // Your code here: need to send a RST segment to the peer } } catch (const exception &e) { std::cerr << "Exception destructing TCP FSM: " << e.what() << std::endl; } }