CS144Lab/libsponge/tcp_connection.cc
2023-02-17 16:22:34 +00:00

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;
}
}