From e70095b9ffa95797742fb29b0c75957d6272b7dd Mon Sep 17 00:00:00 2001 From: Keith Winstein Date: Tue, 28 Sep 2021 17:01:21 -0700 Subject: [PATCH] CS144 Lab checkpoint 3 starter code --- libsponge/tcp_helpers/tcp_config.hh | 35 +++++++++++ libsponge/tcp_helpers/tcp_state.cc | 18 ++++++ libsponge/tcp_helpers/tcp_state.hh | 13 ++++ libsponge/tcp_sender.cc | 38 ++++++++++++ libsponge/tcp_sender.hh | 92 +++++++++++++++++++++++++++++ tests/CMakeLists.txt | 7 +++ writeups/lab3.md | 29 +++++++++ 7 files changed, 232 insertions(+) create mode 100644 libsponge/tcp_helpers/tcp_config.hh create mode 100644 libsponge/tcp_sender.cc create mode 100644 libsponge/tcp_sender.hh create mode 100644 writeups/lab3.md diff --git a/libsponge/tcp_helpers/tcp_config.hh b/libsponge/tcp_helpers/tcp_config.hh new file mode 100644 index 0000000..65542c7 --- /dev/null +++ b/libsponge/tcp_helpers/tcp_config.hh @@ -0,0 +1,35 @@ +#ifndef SPONGE_LIBSPONGE_TCP_CONFIG_HH +#define SPONGE_LIBSPONGE_TCP_CONFIG_HH + +#include "address.hh" +#include "wrapping_integers.hh" + +#include +#include +#include + +//! Config for TCP sender and receiver +class TCPConfig { + public: + static constexpr size_t DEFAULT_CAPACITY = 64000; //!< Default capacity + static constexpr size_t MAX_PAYLOAD_SIZE = 1452; //!< Max TCP payload that fits in either IPv4 or UDP datagram + static constexpr uint16_t TIMEOUT_DFLT = 1000; //!< Default re-transmit timeout is 1 second + static constexpr unsigned MAX_RETX_ATTEMPTS = 8; //!< Maximum re-transmit attempts before giving up + + uint16_t rt_timeout = TIMEOUT_DFLT; //!< Initial value of the retransmission timeout, in milliseconds + size_t recv_capacity = DEFAULT_CAPACITY; //!< Receive capacity, in bytes + size_t send_capacity = DEFAULT_CAPACITY; //!< Sender capacity, in bytes + std::optional fixed_isn{}; +}; + +//! Config for classes derived from FdAdapter +class FdAdapterConfig { + public: + Address source{"0", 0}; //!< Source address and port + Address destination{"0", 0}; //!< Destination address and port + + uint16_t loss_rate_dn = 0; //!< Downlink loss rate (for LossyFdAdapter) + uint16_t loss_rate_up = 0; //!< Uplink loss rate (for LossyFdAdapter) +}; + +#endif // SPONGE_LIBSPONGE_TCP_CONFIG_HH diff --git a/libsponge/tcp_helpers/tcp_state.cc b/libsponge/tcp_helpers/tcp_state.cc index fdded50..4840695 100644 --- a/libsponge/tcp_helpers/tcp_state.cc +++ b/libsponge/tcp_helpers/tcp_state.cc @@ -13,3 +13,21 @@ string TCPState::state_summary(const TCPReceiver &receiver) { return TCPReceiverStateSummary::SYN_RECV; } } + +string TCPState::state_summary(const TCPSender &sender) { + if (sender.stream_in().error()) { + return TCPSenderStateSummary::ERROR; + } else if (sender.next_seqno_absolute() == 0) { + return TCPSenderStateSummary::CLOSED; + } else if (sender.next_seqno_absolute() == sender.bytes_in_flight()) { + return TCPSenderStateSummary::SYN_SENT; + } else if (not sender.stream_in().eof()) { + return TCPSenderStateSummary::SYN_ACKED; + } else if (sender.next_seqno_absolute() < sender.stream_in().bytes_written() + 2) { + return TCPSenderStateSummary::SYN_ACKED; + } else if (sender.bytes_in_flight()) { + return TCPSenderStateSummary::FIN_SENT; + } else { + return TCPSenderStateSummary::FIN_ACKED; + } +} diff --git a/libsponge/tcp_helpers/tcp_state.hh b/libsponge/tcp_helpers/tcp_state.hh index f6ee842..4489e23 100644 --- a/libsponge/tcp_helpers/tcp_state.hh +++ b/libsponge/tcp_helpers/tcp_state.hh @@ -2,6 +2,7 @@ #define SPONGE_LIBSPONGE_TCP_STATE #include "tcp_receiver.hh" +#include "tcp_sender.hh" #include @@ -23,6 +24,9 @@ class TCPState { public: //! \brief Summarize the state of a TCPReceiver in a string static std::string state_summary(const TCPReceiver &receiver); + + //! \brief Summarize the state of a TCPSender in a string + static std::string state_summary(const TCPSender &receiver); }; namespace TCPReceiverStateSummary { @@ -32,4 +36,13 @@ const std::string SYN_RECV = "SYN received (ackno exists), and input to stream h const std::string FIN_RECV = "input to stream has ended"; } // namespace TCPReceiverStateSummary +namespace TCPSenderStateSummary { +const std::string ERROR = "error (connection was reset)"; +const std::string CLOSED = "waiting for stream to begin (no SYN sent)"; +const std::string SYN_SENT = "stream started but nothing acknowledged"; +const std::string SYN_ACKED = "stream ongoing"; +const std::string FIN_SENT = "stream finished (FIN sent) but not fully acknowledged"; +const std::string FIN_ACKED = "stream finished and fully acknowledged"; +} // namespace TCPSenderStateSummary + #endif // SPONGE_LIBSPONGE_TCP_STATE diff --git a/libsponge/tcp_sender.cc b/libsponge/tcp_sender.cc new file mode 100644 index 0000000..30ca8e4 --- /dev/null +++ b/libsponge/tcp_sender.cc @@ -0,0 +1,38 @@ +#include "tcp_sender.hh" + +#include "tcp_config.hh" + +#include + +// Dummy implementation of a TCP sender + +// For Lab 3, please replace with a real implementation that passes the +// automated checks run by `make check_lab3`. + +template +void DUMMY_CODE(Targs &&... /* unused */) {} + +using namespace std; + +//! \param[in] capacity the capacity of the outgoing byte stream +//! \param[in] retx_timeout the initial amount of time to wait before retransmitting the oldest outstanding segment +//! \param[in] fixed_isn the Initial Sequence Number to use, if set (otherwise uses a random ISN) +TCPSender::TCPSender(const size_t capacity, const uint16_t retx_timeout, const std::optional fixed_isn) + : _isn(fixed_isn.value_or(WrappingInt32{random_device()()})) + , _initial_retransmission_timeout{retx_timeout} + , _stream(capacity) {} + +uint64_t TCPSender::bytes_in_flight() const { return {}; } + +void TCPSender::fill_window() {} + +//! \param ackno The remote receiver's ackno (acknowledgment number) +//! \param window_size The remote receiver's advertised window size +void TCPSender::ack_received(const WrappingInt32 ackno, const uint16_t window_size) { DUMMY_CODE(ackno, window_size); } + +//! \param[in] ms_since_last_tick the number of milliseconds since the last call to this method +void TCPSender::tick(const size_t ms_since_last_tick) { DUMMY_CODE(ms_since_last_tick); } + +unsigned int TCPSender::consecutive_retransmissions() const { return {}; } + +void TCPSender::send_empty_segment() {} diff --git a/libsponge/tcp_sender.hh b/libsponge/tcp_sender.hh new file mode 100644 index 0000000..ed0c4fb --- /dev/null +++ b/libsponge/tcp_sender.hh @@ -0,0 +1,92 @@ +#ifndef SPONGE_LIBSPONGE_TCP_SENDER_HH +#define SPONGE_LIBSPONGE_TCP_SENDER_HH + +#include "byte_stream.hh" +#include "tcp_config.hh" +#include "tcp_segment.hh" +#include "wrapping_integers.hh" + +#include +#include + +//! \brief The "sender" part of a TCP implementation. + +//! Accepts a ByteStream, divides it up into segments and sends the +//! segments, keeps track of which segments are still in-flight, +//! maintains the Retransmission Timer, and retransmits in-flight +//! segments if the retransmission timer expires. +class TCPSender { + private: + //! our initial sequence number, the number for our SYN. + WrappingInt32 _isn; + + //! outbound queue of segments that the TCPSender wants sent + std::queue _segments_out{}; + + //! retransmission timer for the connection + unsigned int _initial_retransmission_timeout; + + //! outgoing stream of bytes that have not yet been sent + ByteStream _stream; + + //! the (absolute) sequence number for the next byte to be sent + uint64_t _next_seqno{0}; + + public: + //! Initialize a TCPSender + TCPSender(const size_t capacity = TCPConfig::DEFAULT_CAPACITY, + const uint16_t retx_timeout = TCPConfig::TIMEOUT_DFLT, + const std::optional fixed_isn = {}); + + //! \name "Input" interface for the writer + //!@{ + ByteStream &stream_in() { return _stream; } + const ByteStream &stream_in() const { return _stream; } + //!@} + + //! \name Methods that can cause the TCPSender to send a segment + //!@{ + + //! \brief A new acknowledgment was received + void ack_received(const WrappingInt32 ackno, const uint16_t window_size); + + //! \brief Generate an empty-payload segment (useful for creating empty ACK segments) + void send_empty_segment(); + + //! \brief create and send segments to fill as much of the window as possible + void fill_window(); + + //! \brief Notifies the TCPSender of the passage of time + void tick(const size_t ms_since_last_tick); + //!@} + + //! \name Accessors + //!@{ + + //! \brief How many sequence numbers are occupied by segments sent but not yet acknowledged? + //! \note count is in "sequence space," i.e. SYN and FIN each count for one byte + //! (see TCPSegment::length_in_sequence_space()) + size_t bytes_in_flight() const; + + //! \brief Number of consecutive retransmissions that have occurred in a row + unsigned int consecutive_retransmissions() const; + + //! \brief TCPSegments that the TCPSender has enqueued for transmission. + //! \note These must be dequeued and sent by the TCPConnection, + //! which will need to fill in the fields that are set by the TCPReceiver + //! (ackno and window size) before sending. + std::queue &segments_out() { return _segments_out; } + //!@} + + //! \name What is the next sequence number? (used for testing) + //!@{ + + //! \brief absolute seqno for the next byte to be sent + uint64_t next_seqno_absolute() const { return _next_seqno; } + + //! \brief relative seqno for the next byte to be sent + WrappingInt32 next_seqno() const { return wrap(_next_seqno, _isn); } + //!@} +}; + +#endif // SPONGE_LIBSPONGE_TCP_SENDER_HH diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 477d607..0fff79e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -30,3 +30,10 @@ add_test_exec (recv_window) add_test_exec (recv_reorder) add_test_exec (recv_close) add_test_exec (recv_special) +add_test_exec (send_connect) +add_test_exec (send_transmit) +add_test_exec (send_retx) +add_test_exec (send_ack) +add_test_exec (send_window) +add_test_exec (send_close) +add_test_exec (send_extra) diff --git a/writeups/lab3.md b/writeups/lab3.md new file mode 100644 index 0000000..4579235 --- /dev/null +++ b/writeups/lab3.md @@ -0,0 +1,29 @@ +Lab 3 Writeup +============= + +My name: [your name here] + +My SUNet ID: [your sunetid here] + +I collaborated with: [list sunetids here] + +I would like to thank/reward these classmates for their help: [list sunetids here] + +This lab took me about [n] hours to do. I [did/did not] attend the lab session. + +Program Structure and Design of the TCPSender: +[] + +Implementation Challenges: +[] + +Remaining Bugs: +[] + +- Optional: I had unexpected difficulty with: [describe] + +- Optional: I think you could make this lab better by: [describe] + +- Optional: I was surprised by: [describe] + +- Optional: I'm not sure about: [describe]