zoukankan      html  css  js  c++  java
  • [置顶] NS2中对TCP数据包和ACK包的TCP Sink类的主要实现代码详尽剖析--吐血放送

    NS2中对TCP数据包和ACK包的TCP Sink类的主要实现代码详尽剖析,限于个人水平,如有错误请留言指出!

    TcpSink类的recv()方法:

    void TcpSink::recv(Packet* pkt, Handler*)
    {
    	int numToDeliver;
    	int numBytes = hdr_cmn::access(pkt)->size();//接收到的包的大小
    	// number of bytes in the packet just received
    	hdr_tcp *th = hdr_tcp::access(pkt);//定义接收到的包头位指针
    	/* W.N. Check if packet is from previous incarnation */
    	if (th->ts() < lastreset_) {//说明该包是无效的包
    		// Remove packet and do nothing
    		Packet::free(pkt);//删除该包
    		return;
    	}
    	acker_->update_ts(th->seqno(),th->ts(),ts_echo_rfc1323_);//更新接收端确认器,更新内容:包的序列号、到达时间,相应的时间戳
    	// update the timestamp to echo
    	
          	numToDeliver = acker_->update(th->seqno(), numBytes);//把更新的序列号和字节数,给变量numToDeliver用于计算recv窗口
    	// update the recv window; figure out how many in-order-bytes
    	// (if any) can be removed from the window and handed to the
    	// application
    	if (numToDeliver) {//应用程序对变量numToDeliver处理
    		bytes_ += numToDeliver;
    		recvBytes(numToDeliver);
    	}
    	// send any packets to the application
          	ack(pkt);//回应该包的ACK
    	// ACK the packet
    	Packet::free(pkt);
    	// remove it from the system
    }
    


    TcpSink类的ack()方法:

    void TcpSink::ack(Packet* opkt)
    {
    	Packet* npkt = allocpkt();//opkt是指刚接收到的数据包,npkt是即将构建的该数据包的对应ACK包
    	// opkt is the "old" packet that was received
    	// npkt is the "new" packet being constructed (for the ACK)
    	double now = Scheduler::instance().clock();//获取当前时间用于ACK包
    
    	hdr_tcp *otcp = hdr_tcp::access(opkt);//接收到的数据包的TCP包头指针
    	hdr_ip *oiph = hdr_ip::access(opkt);//接收到的数据包的IP包头指针
    	hdr_tcp *ntcp = hdr_tcp::access(npkt);//将要构建的ACK包的TCP包头指针
    
    	if (qs_enabled_) {//如果可以进行快速启动,进行以下相关处理
    		// QuickStart code from Srikanth Sundarrajan.
    		hdr_qs *oqsh = hdr_qs::access(opkt);
    		hdr_qs *nqsh = hdr_qs::access(npkt);
    	        if (otcp->seqno() == 0 && oqsh->flag() == QS_REQUEST) {
    	                nqsh->flag() = QS_RESPONSE;
    	                nqsh->ttl() = (oiph->ttl() - oqsh->ttl()) % 256;
    	                nqsh->rate() = oqsh->rate(); 
    	        }
    	        else {
    	                nqsh->flag() = QS_DISABLE;
    	        }
    	}
    
    
    	// get the tcp headers
    	ntcp->seqno() = acker_->Seqno();//序列号填充ACK???
    	// get the cumulative sequence number to put in the ACK; this is just the left edge of the receive window - 1
    	ntcp->ts() = now;//将时间填充到ACK的TCP包头
    	// timestamp the packet
    
    	if (ts_echo_bugfix_)  /* TCP/IP Illustrated, Vol. 2, pg. 870 */  //以下对是否启用时间戳的处理
    		ntcp->ts_echo() = acker_->ts_to_echo();
    	else
    		ntcp->ts_echo() = otcp->ts();
    	// echo the original's time stamp
    
    	hdr_ip* oip = hdr_ip::access(opkt);//接收到的数据包的IP包头指针
    	hdr_ip* nip = hdr_ip::access(npkt);//用于构建的ACK包的IP包头指针
    	// get the ip headers
    	nip->flowid() = oip->flowid();//接收到的数据包的IP包的ID赋给构建的ACK包的IP包头
    	// copy the flow id
    	
    	hdr_flags* of = hdr_flags::access(opkt);//接收到的数据包的包标记指针
    	hdr_flags* nf = hdr_flags::access(npkt);//构建的ACK包的包标记指针
    	hdr_flags* sf; //接收端已经存储的包标记指针,主要用于对延迟包的处理,无需详细解释
    	if (save_ != NULL)
    		sf = hdr_flags::access(save_);
    	else 
    		sf = 0;
    		// Look at delayed packet being acked. 
    	if ( (sf != 0 && sf->cong_action()) || of->cong_action() ) //如果接收端有已经存储的包标记且该标记的拥塞响应位为真或者刚收到的新数据包的拥塞响应位为真,后一种情况是研究重点
    		// Sender has responsed to congestion. 
    		acker_->update_ecn_unacked(0);//确认器将未确认的拥塞指示置为已经确认(即没有没确认,有点拗口,但就是这样理解)
    	if ( (sf != 0 && sf->ect() && sf->ce())  || (of->ect() && of->ce()) )//如果接收端有已经存储的包标记且该标记的ECT位和CE位都为真或者刚收到的数据包的标记的ECT位和CE位都为真,也就是说该包到达接收端的过程中经历量拥塞,后一种情况是研究重点
    		// New report of congestion.  
    		acker_->update_ecn_unacked(1);//确认器将未确认的拥塞指示置为未确认
    	if ( (sf != 0 && sf->ect()) || of->ect() )//如果接收端有已经存储的包标记且该包标记的ECT位为真或者刚收到的新数据包的标记的ECT位为真,此时并没有考虑CE位的真假,后者为研究重点
    		// Set EcnEcho bit.  
    		nf->ecnecho() = acker_->ecn_unacked();//将确认器的已经存在的位赋值给即将构建的ACK包的标记位ECN-Echo
    	if (!of->ect() && of->ecnecho() || (sf != 0 && !sf->ect() && sf->ecnecho()) ) {//如果刚收到的新的数据包的标记的ECT位为假且ECN-Echo位为真或者接收端有已经存储的包标记且该标记的ECT位位假且ECN-Echo位为真,简而言之,该包到达接收端过程中没有经历拥塞,同时对响应的带有ECE位的ACK做出了响应
    		 // This is the negotiation for ECN-capability.
    		 // We are not checking for of->cong_action() also. 
    		 // In this respect, this does not conform to the specifications in the internet draft 
    		nf->ecnecho() = 1;//直接将即将构建的ACK包的标记的ECN-Echo位置1
    		if (ecn_syn_) //如果是TCP连接刚建立时
    			nf->ect() = 1; //将即将构建的ACK包的标记的ECT位置1,不是研究重点
    	}
            //下面两行是对非对称的TCP连接的特殊处理,无需研究
    	acker_->append_ack(hdr_cmn::access(npkt),ntcp, otcp->seqno());
    	add_to_ack(npkt);
    	// the above function is used in TcpAsymSink
    
            // Andrei Gurtov 用于记录序列号
            acker_->last_ack_sent_ = ntcp->seqno();
            // printf("ACK %d ts %f
    ", ntcp->seqno(), ntcp->ts_echo());
    	
    	send(npkt, 0);//发送该ACK包
    	// send it
    }
    


     

  • 相关阅读:
    C++ primer plus读书笔记——第6章 分支语句和逻辑运算符
    C++ primer plus读书笔记——第7章 函数——C++的编程模块
    C++ primer plus读书笔记——第5章 循环和关系表达式
    C++ primer plus读书笔记——第4章 复合类型
    C++ primer plus读书笔记——第3章 处理数据
    C++ primer plus读书笔记——第2章 开始学习C++
    10款好用到爆的Vim插件,你知道几个?
    程序员最讨厌的100件事,瞬间笑喷了,哈哈~~
    20 个最常用的 Git 命令用法说明及示例
    史上最全的Nginx配置参数中文说明
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3199022.html
Copyright © 2011-2022 走看看