forked from PKUFlyingPig/CS144-Computer-Network
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
1,434 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
#ifndef SPONGE_LIBSPONGE_TCP_CONFIG_HH | ||
#define SPONGE_LIBSPONGE_TCP_CONFIG_HH | ||
|
||
#include "address.hh" | ||
#include "wrapping_integers.hh" | ||
|
||
#include <cstddef> | ||
#include <cstdint> | ||
#include <optional> | ||
|
||
//! 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<WrappingInt32> 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
#include "tcp_sender.hh" | ||
|
||
#include "tcp_config.hh" | ||
|
||
#include <random> | ||
|
||
// 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 <typename... Targs> | ||
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<WrappingInt32> 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() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <functional> | ||
#include <queue> | ||
|
||
//! \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<TCPSegment> _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<WrappingInt32> 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<TCPSegment> &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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
#include "sender_harness.hh" | ||
#include "wrapping_integers.hh" | ||
|
||
#include <cstdint> | ||
#include <cstdlib> | ||
#include <exception> | ||
#include <iostream> | ||
#include <optional> | ||
#include <stdexcept> | ||
#include <string> | ||
|
||
using namespace std; | ||
|
||
int main() { | ||
try { | ||
auto rd = get_random_generator(); | ||
|
||
{ | ||
TCPConfig cfg; | ||
WrappingInt32 isn(rd()); | ||
cfg.fixed_isn = isn; | ||
|
||
TCPSenderTestHarness test{"Repeat ACK is ignored", cfg}; | ||
test.execute(ExpectSegment{}.with_no_flags().with_syn(true).with_payload_size(0).with_seqno(isn)); | ||
test.execute(ExpectNoSegment{}); | ||
test.execute(AckReceived{WrappingInt32{isn + 1}}); | ||
test.execute(WriteBytes{"a"}); | ||
test.execute(ExpectSegment{}.with_no_flags().with_data("a")); | ||
test.execute(ExpectNoSegment{}); | ||
test.execute(AckReceived{WrappingInt32{isn + 1}}); | ||
test.execute(ExpectNoSegment{}); | ||
} | ||
|
||
{ | ||
TCPConfig cfg; | ||
WrappingInt32 isn(rd()); | ||
cfg.fixed_isn = isn; | ||
|
||
TCPSenderTestHarness test{"Old ACK is ignored", cfg}; | ||
test.execute(ExpectSegment{}.with_no_flags().with_syn(true).with_payload_size(0).with_seqno(isn)); | ||
test.execute(ExpectNoSegment{}); | ||
test.execute(AckReceived{WrappingInt32{isn + 1}}); | ||
test.execute(WriteBytes{"a"}); | ||
test.execute(ExpectSegment{}.with_no_flags().with_data("a")); | ||
test.execute(ExpectNoSegment{}); | ||
test.execute(AckReceived{WrappingInt32{isn + 2}}); | ||
test.execute(ExpectNoSegment{}); | ||
test.execute(WriteBytes{"b"}); | ||
test.execute(ExpectSegment{}.with_no_flags().with_data("b")); | ||
test.execute(ExpectNoSegment{}); | ||
test.execute(AckReceived{WrappingInt32{isn + 1}}); | ||
test.execute(ExpectNoSegment{}); | ||
} | ||
|
||
/* remove requirement to send corrective ACK for bad ACK | ||
{ | ||
TCPConfig cfg; | ||
WrappingInt32 isn(rd()); | ||
cfg.fixed_isn = isn; | ||
TCPSenderTestHarness test{"Early ACK results in bare ACK", cfg}; | ||
test.execute(ExpectSegment{}.with_no_flags().with_syn(true).with_payload_size(0).with_seqno(isn)); | ||
test.execute(ExpectNoSegment{}); | ||
test.execute(AckReceived{WrappingInt32{isn + 1}}); | ||
test.execute(WriteBytes{"a"}); | ||
test.execute(ExpectSegment{}.with_no_flags().with_data("a")); | ||
test.execute(ExpectNoSegment{}); | ||
test.execute(AckReceived{WrappingInt32{isn + 17}}); | ||
test.execute(ExpectSegment{}.with_seqno(isn + 2)); | ||
test.execute(ExpectNoSegment{}); | ||
} | ||
*/ | ||
|
||
} catch (const exception &e) { | ||
cerr << e.what() << endl; | ||
return 1; | ||
} | ||
|
||
return EXIT_SUCCESS; | ||
} |
Oops, something went wrong.