107 lines
3.5 KiB
C++
107 lines
3.5 KiB
C++
#include "tcp_connection.hh"
|
|
|
|
#include <iostream>
|
|
|
|
// 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 <typename... Targs>
|
|
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
|
|
if (seg.length_in_sequence_space() && _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();
|
|
// TODO: Keep-alive but it seems to be use
|
|
// recver in FIN_RECV
|
|
// sender in SYN_ACKED
|
|
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) {
|
|
_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()) &&
|
|
(_receiver.stream_out().input_ended() && !_receiver.unassembled_bytes()) &&
|
|
(!_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;
|
|
}
|
|
}
|