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\n", ntcp->seqno(), ntcp->ts_echo());
send(npkt, 0);//發送該ACK包
// send it
}
