当前位置:网站首页>[Stanford Jiwang cs144 project] lab3: tcpsender

[Stanford Jiwang cs144 project] lab3: tcpsender

2022-07-07 07:43:00 Altair_ alpha

In this section TCP The sender in the protocol TCPSender The implementation of the .Sender Have an input ByteStream, On behalf of the data provided by the user to be sent , and Sender Be responsible for assembling it into TCP Packet and send it out . So called sending , That is to put the data package push To a queue _segments_out Then you can , The next section realizes TCPConnection class ( That is to say Sender and Receiver Owner ) It will be responsible for taking packets from the queue and actually sending them .

Except for the guarantee TCPSegment Data in the 、 Serial number and SYN,FIN Wait until the logo is set correctly ,Sender There are two main issues to consider . First, the receiving window ,Receiver The amount of data that can be received at one time is limited , Will pass Header in Window Size Field to Sender Dynamic feedback of this information .Sender It should be ensured that ( That is, it has been sent and has not been confirmed to receive ) The amount of data is less than or equal to this window .

Second, data retransmission , Because the data packet may be lost during transmission ,Sender You cannot discard a packet immediately after sending it , Instead, it is placed in the temporary storage area ( be called outstanding data), If you haven't received it within a period of time Receiver Confirmation signal of ( adopt ackno) You need to retransmit . The handout stipulates that it will be true TCP A series of rules after the agreement is slightly simplified are as follows :

  1. Perception of time . In order to ensure the certainty of program testing ,Sender The implementation does not actively call any actual time API, Instead, it passively invokes its tick The function perceives the passage of time , The parameter of this function is from the last call tick The number of milliseconds elapsed after .
  2. The concept of timer . There needs to be a Timer, It can be activated , Triggered after a set time interval , It can also be stopped manually . Because the perception of time is passive , therefore Timer It can only be in tick Function .
  3. Sender Accept an initial Retransmission timeout (retransmission timeout, RTO) Parameters , This initial parameter is fixed , At the same time, there is a current RTO Parameters . Every time a packet is sent , If Timer Not activated , Start and make it current RTO Triggered in milliseconds . When all data in the staging area is ack when , stop it Timer.
  4. When Timer stay tick When triggered in , Will not be completely ack Of One of the earliest ( That is, the one with the smallest serial number ) Temporary packet retransmission . meanwhile , If the window size is not 0, Record the number of consecutive retransmissions , This information will be implemented in the next section TCPConnection Used to determine whether to end the connection , and Make current RTO Double ( Index retreat ). Then according to the current RTO( Probably just updated ) start-up Timer.
  5. When receiving a request for newly sent data ack when , Will the current RTO Reset to initial RTO, And set the number of consecutive retransmissions to zero . If the staging area is not empty , start-up Timer.

Implementation , Mainly Timer + Three main functions . Refer to the code of many students on the Internet Timer The function is put in Sender In the class implementation , I still have a separate class here as follows :

// Helper class to determine whether a given timeout has reached (i.e., expired) since started.
// It won't emit any signal but provide accessor for caller to check its state
// Also the class won't call any system time function but rely on its update() called to
// know time elapsed and whether timeout is reached.
class Timer {
    
  public:
    // start the timer with given timeout.
    // call start() on a started timer acts like reset() called first
    void start(unsigned int timeout);
    // update the timer with info of time elapsed since start/last update
    void update(unsigned int time_elapsed);
    // reset(stop) the timer
    void reset();
    // check whether the timer is active
    bool active() const {
     return _active; }
    // check whether the timer has timed out (if the timer is inactive, return value is false)
    bool expired() const {
     return _active && _expired; }

  private:
    bool _active{
    false};
    bool _expired{
    false};
    unsigned int _current_time{
    0};
    unsigned int _timeout{
    0};
};

One of the main functions : void ack_received(const WrappingInt32 ackno, const uint16_t window_size). The window size is determined by the members of a class _window_size Record updates , This parameter is used in the following sending function .ackno the unwrap Then compare with the packet number in the temporary storage area one by one , Remove the completely confirmed packets ( Note that the packet number in the staging area can be used to order from small to large ). If you can remove , Reset by rule RTO And retransmission count . If the staging area is empty after removal , take Timer stop it . Because the temporary storage area may be vacated , Last call fill_window( See below ) Try sending a new packet . The code is as follows :

void TCPSender::ack_received(const WrappingInt32 ackno, const uint16_t window_size) {
    
    _window_size = window_size;
    // use next seqno as checkpoint
    uint64_t ack_seqno = unwrap(ackno, _isn, _next_seqno);
    // it's impossible that ackno > _next_seqno, because that byte hasn't been sent yet!
    if (ack_seqno > _next_seqno) {
    
        return;
    }

    // remove completely ack-ed segments from the retransmission buffer
    // because the segment in retrans buffer is ordered by seqno,
    // it's ok to break once current seg can't be erased (subsequent seg has larger seqno)
    for (auto it = _retrans_buf.begin(); it != _retrans_buf.end();) {
    
        if (unwrap((*it).header().seqno, _isn, _next_seqno) + (*it).length_in_sequence_space() <= ack_seqno) {
    
            it = _retrans_buf.erase(it);
            _retrans_timeout = _initial_retransmission_timeout;
            _timer.start(_retrans_timeout);
            _consec_retrans_count = 0;
        } else
            break;
    }
    // stop the timer if retransmission buffer is clear
    if (_retrans_buf.empty())
        _timer.reset();

    // refill the window
    fill_window();
}

The second of the main functions :void tick(const size_t ms_since_last_tick). If Timer Started , Then update the time according to the parameters , Then check whether it triggers . If the trigger , Then retransmit according to the rules , Increase retransmission count , send RTO Double , And restart Timer. The code is as follows :

void TCPSender::tick(const size_t ms_since_last_tick) {
    
    if (_timer.active())
        _timer.update(ms_since_last_tick);
    if (_timer.expired()) {
    
        _segments_out.emplace(_retrans_buf.front());
        if (_window_size > 0) {
    
            _consec_retrans_count++;
            _retrans_timeout *= 2;
        }
        _timer.start(_retrans_timeout);
    }
}

The third of the main functions :void fill_window(). That is, the one mentioned at the beginning “ send data 、 Serial number and SYN,FIN Wait for the packet with the correct identification setting ”. Generally speaking, the process is : Determine whether the window is not filled → If it is not filled , Make one TCPSegment, Specifically, it is divided into judgment and release SIN identification , Put the data , Judge release FIN Identify three steps → take TCPSegment Add to _segments_out And staging area , Update the serial number and remaining space . Cycle until the window is filled . See the following source file for details , Don't paste it .

Full code link :
tcp_sender.hh
tcp_sender.cc

Screenshot of customs clearance
 Insert picture description here

原网站

版权声明
本文为[Altair_ alpha]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/188/202207070413448980.html