我发现一个现象,也就在中国,会有某某代码分析,某某代码走读。国外似乎就没有这种现象。
没有法子,想在ns2上做点自己东西,不添加C++代码估计是办不到的。之前我分析了quic中一个小仿真器的大致原理[1]。switch对象中的队列长度为什么增长?进的数据包多,而发送数据包少。就是交换机对象的发送队列在每次发包的之后会检测,上次发送数据包需要耗费的时间,就是网卡的处理能力(packet_length/bandwidth)。我上传的quic_simulator[1],queue.cc在收到一个数据包,文件中有这样一行代码Schedule(clock_->Now()+tx_port_->TimeUntilAvailable());就是经过上次数据包处理延时,本次数据包才能进入网卡,发送到链路上Queue::Act(){tx_port_->AcceptPacket(...)}。
quic仿真器是这样的工作原理,ns2也不例外。
仿真数据包传递流程,node->queue->delaylink。 ns2仿真链路上的队列ns-2.35\queue\queue.cc接收数据包函数
void Queue::recv(Packet* p, Handler*)
{
double now = Scheduler::instance().clock();
enque(p);
if (!blocked_) {
/*
* We're not blocked. Get a packet and send it on.
* We perform an extra check because the queue
* might drop the packet even if it was
* previously empty! (e.g., RED can do this.)
*/
p = deque();
if (p != 0) {
utilUpdate(last_change_, now, blocked_);
last_change_ = now;
blocked_ = 1;
target_->recv(p, &qh_);
}
}
}
第一次接收到数据包,enque(p)将数据包加入队列,之后p = deque()将数据包弹出队列,blocked_ = 1设置为阻塞,target_->recv(p, &qh_);将数据包发送到延时链路上,同时这个函数传递个一个队列句柄指针qh_。
ns-2.35\link\delay.cc
void LinkDelay::recv(Packet* p, Handler* h)
{
double txt = txtime(p);
Scheduler& s = Scheduler::instance();
if (dynamic_) {
Event* e = (Event*)p;
e->time_= txt + delay_;
itq_->enque(p); // for convinience, use a queue to store packets in transit
s.schedule(this, p, txt + delay_);
} else if (avoidReordering_) {
// code from Andrei Gurtov, to prevent reordering on
// bandwidth or delay changes
double now_ = Scheduler::instance().clock();
if (txt + delay_ < latest_time_ - now_ && latest_time_ > 0) {
latest_time_+=txt;
s.schedule(target_, p, latest_time_ - now_ );
} else {
latest_time_ = now_ + txt + delay_;
s.schedule(target_, p, txt + delay_);
}
} else {
s.schedule(target_, p, txt + delay_);
}
s.schedule(h, &intr_, txt);
}
数据包进入链路后,链路会计算数据在网卡上的发送时间。在recv函数中,将数据包的网卡处理时延和这个句柄注册到scheduler(s.schedule(h, &intr_, txt);)。
这个时刻到来,调度器回调队列句柄,进行出队操作。ns-2.35\queue\queue.cc:
void QueueHandler::handle(Event*)
{
queue_.resume();
}
void Queue::resume()
{
double now = Scheduler::instance().clock();
Packet* p = deque();
if (p != 0) {
target_->recv(p, &qh_);
} else {
if (unblock_on_resume_) {
utilUpdate(last_change_, now, blocked_);
last_change_ = now;
blocked_ = 0;
}
else {
utilUpdate(last_change_, now, blocked_);
last_change_ = now;
blocked_ = 1;
}
}
}
如果在这个网卡处理时延之间有数据包到来,就到进队等待,队列长度就会增加。