您的位置:首页 > 理论基础 > 计算机网络

TCP Reno成员函数的解释(中文)好东西大家分享

2011-01-06 16:38 225 查看
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
 * Copyright (c) 1990, 1997 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by the University of California, Lawrence Berkeley Laboratory,
 * Berkeley, CA.  The name of the University may not be used to
 * endorse or promote products derived from this software without
 * specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#ifndef lint
static const char rcsid[] =
    "@(#) $Header: /nfs/jade/vint/CVSROOT/ns-2/tcp/tcp-reno.cc,v 1.42 2005/07/13 03:51:32 tomh Exp $ (LBL)";
#endif

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>

#include "ip.h"
#include "tcp.h"
#include "flags.h"

static class RenoTcpClass : public TclClass {
public:
 RenoTcpClass() : TclClass("Agent/TCP/Reno") {}
 TclObject* create(int, const char*const*) {
  return (new RenoTcpAgent());
 }
} class_reno;

int RenoTcpAgent::window()
{
 //
 // reno: inflate the window by dupwnd_
 // dupwnd_ will be non-zero during fast recovery,
 // at which time it contains the number of dup acks
 //
 int win = int(cwnd_) + dupwnd_;     //在快速恢复时,增加发送窗口,dupwnd_=0(初始)或者=numdupacks_,在dupack_>numdupacks_时,dupwnd_每次还要+1
 if (frto_ == 2) {
  // First ack after RTO has arrived.
  // Open window to allow two new segments out with F-RTO.
  win = force_wnd(2);
 }
 if (win > int(wnd_))
  win = int(wnd_);
 return (win);
}

double RenoTcpAgent::windowd()
{
 //
 // reno: inflate the window by dupwnd_
 // dupwnd_ will be non-zero during fast recovery,
 // at which time it contains the number of dup acks
 //
 double win = cwnd_ + dupwnd_;
 if (win > wnd_)
  win = wnd_;
 return (win);
}

RenoTcpAgent::RenoTcpAgent() : TcpAgent(), dupwnd_(0)
{
}

void RenoTcpAgent::recv(Packet *pkt, Handler*)
{
 hdr_tcp *tcph = hdr_tcp::access(pkt);
 int valid_ack = 0;
        if (qs_approved_ == 1 && tcph->seqno() > last_ack_)
  endQuickStart();
        if (qs_requested_ == 1)
                processQuickStart(pkt);
#ifdef notdef
 if (pkt->type_ != PT_ACK) {
  fprintf(stderr,
   "ns: confiuration error: tcp received non-ack/n");
  exit(1);
 }
#endif
        /* W.N.: check if this is from a previous incarnation */
        if (tcph->ts() < lastreset_) {
                // Remove packet and do nothing
                Packet::free(pkt);
                return;
        }
 ++nackpack_;
 ts_peer_ = tcph->ts();

 if (hdr_flags::access(pkt)->ecnecho() && ecn_)
  ecn(tcph->seqno());
 recv_helper(pkt);
 recv_frto_helper(pkt);
 if (tcph->seqno() > last_ack_) {                //本ACK序号>上次记录的ACK序号,表示是新的ACK
  if (last_cwnd_action_ == CWND_ACTION_DUPACK)         //*本次新ACK,则退出快速重传
   last_cwnd_action_ = CWND_ACTION_EXITED;      //*
  dupwnd_ = 0;                                         //*设置dupwnd_=0,用于下次计算发送窗口win
  recv_newack_helper(pkt);
  if (last_ack_ == 0 && delay_growth_) {            //是连接以来的第一个ACK
   cwnd_ = initial_window();                 //初始化CWND
  }
 } else if (tcph->seqno() == last_ack_) {               // 本次不是新ACK,是上次的重复ACK
  if (hdr_flags::access(pkt)->eln_ && eln_) {     
   tcp_eln(pkt);
   return;
  }
  //以下是重复数ACK=1-2、3和3以上的三种情况的处理
  if (++dupacks_ == numdupacks_) {              //*默认numdupacks_=3,有3个重传就执行快速恢复和快速重传
   dupack_action();                       //改变SSTHRESH、CWND,并重设定时器超时
   if (!exitFastRetrans_)   //*exitFastRetrans_默认为1,reno专用
    dupwnd_ = numdupacks_;
  } else if (dupacks_ > numdupacks_ && (!exitFastRetrans_   //*超过numdupacks_次重传ACK,且上次也是重传ACK
       || last_cwnd_action_ == CWND_ACTION_DUPACK )) {
   ++dupwnd_; //* 执行快速恢复:在超过numdupacks_次重传ACK后,每次再收到一个重复ACK,使发送窗口+1,加快发送,fast recovery
  } else if (dupacks_ < numdupacks_ && singledup_ ) {  //如果是1次到2次重复ACK,发送一个新分组 ,这是限制传输机制
   send_one();
  }
 }
        if (tcph->seqno() >= last_ack_)       //本ACK序号>=上次ACK,表示是新ACK或重复ACK,是合法的
                // Check if ACK is valid.  Suggestion by Mark Allman.
                valid_ack = 1;
 Packet::free(pkt);              //合法,回收该分组内存
#ifdef notyet
 if (trace_)
  plot();
#endif

 /*
  * Try to send more data
  */

        if (valid_ack || aggressive_maxburst_)   //aggressive_maxburst_默认值为1
  if (dupacks_ == 0 || dupacks_ > numdupacks_ - 1)   //*如果是新的ACK或者重复ACK数>=3,尽量发送更多的数据
   send_much(0, 0, maxburst_);              //即如果重复ACK=1或者2,则不这样传,而按照上面只发送一个分组
}
/*注: 限制传输机制(Limited Transmit mechanism)
    我们知道,当前的TCP应用主要有两种重传机制-快速重传和超时重传。当TCP源端收到3个ACK副本时,
就会触发快速重传机制,此时源端重传丢失的数据包并且将拥塞窗口大小减半。这种情况下,TCP流往往能
够很快从丢包中恢复过来,重新回到原先的发送速率。但如果TCP源端没有收到3个ACK副本,例如拥塞窗口
大小小于4,那么TCP源端则需要等待相当长时间,以便超时重发。这样,小窗口的TCP流就很容易陷入不必
要的超时重发,使其吞吐量大大下降。
    为了避免这种不必要的超时重传,一种改进办法就是只要TCP源端收到一个或者两个ACK副本,并且如果
通告窗口允许,便继续发送新的数据包。这是因为只要收到ACK副本,就表明有数据包已经离开网络被接受
端接收了,而此时源端还无法判断数据包是否被丢弃,根据“数据包守恒”原则,只要遵守拥塞窗口的规范,
也即同时在网络中传送的数据包数量不能超过拥塞窗口的大小(以数据包为单位),源端就可以继续发送新
的数据包
4000
。这种机制称为限制传输机制(Limited Transmit mechanism),这种机制对排序的数据包尤其有效。
    限制传输机制可以使小窗口的TCP流很快从丢包中恢复过来。例如,对于拥塞窗口大小为4地TCP流,如果
其第二个数据包丢失,那么按传统地做法需要等待超时重传。而在限制传输机制下,当源端收到对第三个数据
包确认的ACK副本时(ACK中要求源端发送第二个数据包),继续发送新的数据包,最终源端可以收到三个ACK
副本从而触发快速重传,从而减少了不必要的超时重传。

*/

 

 

//last_cwnd_action_即上次对CWND的动作为DUPACK,则本次允许快速重传   //本次
int
RenoTcpAgent::allow_fast_retransmit(int last_cwnd_action_)
{
 return (last_cwnd_action_ == CWND_ACTION_DUPACK);
}

/*
 * Dupack-action: what to do on a DUP ACK.  After the initial check
 * of 'recover' below, this function implements the following truth
 * table:
 * 
 *      bugfix  ecn     last-cwnd == ecn        action 
 * 
 *      0       0       0                       reno_action
 *      0       0       1                       reno_action    [impossible]
 *      0       1       0                       reno_action
 *      0       1       1                       retransmit, return 
 *      1       0       0                       nothing
 *      1       0       1                       nothing        [impossible]
 *      1       1       0                       nothing
 *      1       1       1                       retransmit, return
 */
 //重复ACK满3个,执行下面程序  
void
RenoTcpAgent::dupack_action()
{
 int recovered = (highest_ack_ > recover_);
 int allowFastRetransmit = allow_fast_retransmit(last_cwnd_action_); //*上次是DUPACK状态,本次仍是重复ACK,允许本次快速重传
        if (recovered || (!bug_fix_ && !ecn_) || allowFastRetransmit) {  //*若允许快速重传,跳转
  goto reno_action;
 }

 if (ecn_ && last_cwnd_action_ == CWND_ACTION_ECN) {
  last_cwnd_action_ = CWND_ACTION_DUPACK;
  /*
   * What if there is a DUPACK action followed closely by ECN
   * followed closely by a DUPACK action?
   * The optimal thing to do would be to remember all
   * congestion actions from the most recent window
   * of data.  Otherwise "bugfix" might not prevent
   * all unnecessary Fast Retransmits.
   */
  reset_rtx_timer(1,0);
  output(last_ack_ + 1, TCP_REASON_DUPACK);
  dupwnd_ = numdupacks_;
  return;
 }

 if (bug_fix_) {
  /*
   * The line below, for "bug_fix_" true, avoids
   * problems with multiple fast retransmits in one
   * window of data.
   */
  return;
 }

reno_action:
 // we are now going to fast-retransmit and will trace that event
 trace_event("RENO_FAST_RETX");            //*跟踪RENO快速重传
 recover_ = maxseq_;                       //更新recover_
 last_cwnd_action_ = CWND_ACTION_DUPACK;   //记录本次重复ACK
 slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_HALF);   //*SSTHRESH和CWND都降低一半
 reset_rtx_timer(1,0);              //*重设重传定时器,mild=1,使t_seqno_ != highest_ack_ + 1,保持原来的值
 output(last_ack_ + 1, TCP_REASON_DUPACK); //*发送下一个未ACK的分组, from top
        dupwnd_ = numdupacks_;         //*发送窗口增加数
 return;
}

void RenoTcpAgent::timeout(int tno)
{
 if (tno == TCP_TIMER_RTX) {
  dupwnd_ = 0;
  dupacks_ = 0;
  if (bug_fix_) recover_ = maxseq_;
  TcpAgent::timeout(tno);
 } else {
  timeout_nonrtx(tno);
 }
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息