#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) { // god knows whether it should be active or not initially _time_last_segment_received = 0; if (seg.header().rst) { _set_rst(); return; } _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(); !! This should be done in ack_received } // if the inbound seg has len it should be responded // except that ack_received() has send a seg, so no more is needed // Keep-alive is suggested in RFC1122, but it is optional, though implemented here if ((seg.length_in_sequence_space() || (_receiver.ackno().has_value() && seg.header().seqno == _receiver.ackno().value() - 1)) && _sender.segments_out().empty()) { _sender.fill_window(); // it is necessary to repond to SYN with a SYNACK if (_sender.segments_out().empty()) { _sender.send_empty_segment(); } } _send_all(); // recver in FIN_RECV // sender in SYN_ACKED if (_receiver.stream_out().input_ended() && (!_sender.stream_in().eof() || _sender.next_seqno_absolute() < _sender.stream_in().bytes_written() + 2)) { _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) { _send_rst(); _set_rst(); return; } _send_all(); // actually, this long if could be represented by TCP states // for sender, it is the FIN_ACKED state // for recver, it is the FIN_RECV state // and plus linger if ((!_sender.bytes_in_flight() && _sender.stream_in().eof() && _sender.next_seqno_absolute() == _sender.stream_in().bytes_written() + 2) && (_receiver.stream_out().input_ended()) && (!_linger_after_streams_finish || _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"; _send_rst(); _set_rst(); // 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; } }