CS144Lab/libsponge/tcp_connection.cc
2023-02-18 05:38:32 +00:00

111 lines
3.8 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
// 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;
}
}