NS 发送一个 cbr 数据包的过程
1.
固定网络的仿真是通过下面三层合作来实现的。
Application这个层是实现数据流的层次。 Agent 这个层是实现所有各层协议的的层次。 Node 这个部分由多个分类器( Classifier )实现了所有接收数据包进行判断是否进行转发或接收到 Agent 的部分。 Link 实现了队列、时延、 Agent 、记录 Trace 等一系列的仿真问题。
2.
TclObject |
Handler |
ParentNode |
Process |
NsObject |
Node |
Application |
Connector |
Classifier |
Delay |
Queue |
Agent |
Trace |
AddressClassifier |
图表2 NS 内部类的继承关系图 |
此图为NS 内部类的继承关系,可以看出一些类是由那些类继承来的,这样相同的属性调用的函数就可以很方便的找到出处。
3.
在用 gdb 跟踪 NS发送一个 cbr 数据包的过程可以看到一下顺序:
CBR_Traffic::start
TrafficGenerator::timeout
Application::send
UdpAgent::sendmsg
Classifier::recv
Connect::recv
Connect::send
Trace::recv
Connector::send
Queue::recv
DropTail::enque
DequeTrace::recv
Connector::send
LinkDelay::recv
Scheduler::dispatch
Scheduler::run
从上面的顺序可以看出,数据包在发送以后先通过应用层( Application::send)进行发送;然后通过 Agent 层( UdpAgent::sendmsg), UdpAgent 是在初始化 Agent 的时候确定的。 UdpAgent 还有一个作用是生成相应的数据包;然后进入 Node 部分的分类器 Classifier ( Classifier::recv),通过 find ()函数返回下一跳地址。这个函数是通过读取 Packet 的内容得到下一跳地址的,返回给 recv 函数后调用 node->recv()进入 connector ;经过 connector::recv和 connector::send后确定数据包可发,进入 Trace::recv,记录这个数据包加入队列的记录;之后通过connector::send,进入 Queue::recv函数,将数据包正式加入发送队列,再根据已经设定好的方法确定加入队列是否成功,是否要被丢弃;再调用DequeTrace::recv记录数据包弹出队列的记录;再通过 connector::send进入 LinkDelay::recv,先判断目的节点是否可达,根据不同的结果将事件写入scheduler ,等待按序执行。
上述的过程只是一个数据包从生成到发送出去的过程,因为 NS 是一个根据一个一个离散事件调度执行的,后面的过程用 gdb 跟不进去。但可以看出,数据包是发送给下一跳节点,可知数据包是通过每个中间节点的。
4.
4.1.
开始从下面函数进入CBR 数据源发送:
void CBR_Traffic::start()
{
}
voidTrafficGenerator::timeout()
{
}
4.2.
void Application::send(intnbytes)
{
}
void Agent::sendmsg(int , AppData* , const char*)
{
}
上述两个函数其实并没有什么实质的操作,只是这样可以看出其经过了应用层和Agent 层。
4.3.
void Classifier::recv(Packet* p,Handler*h)
{
}
NsObject* Classifier::find(Packet*p)
{
}
这个地址分类器就是根据数据包的内容,通过偏移查找接收的节点,然后调用接收节点的recv 函数。而 find 函数是根据数据包的内容得到 slot 的值从而查询出谁是接收方的 node 。
4.4.
void Connector::recv(Packet* p, Handler*h)
{
}
inline void send(Packet* p, Handler* h) {target_->recv(p, h); }
connector 的 recv 和 send 函数是一个接口。这个函数中最重要的是 target_ 这个值,这个值的不同会不同的调用 Trace::recv、 Queue::recv、 LinkDelay::recv等等,但是这个值在那确定还没有看出来。
4.5.
void Queue::recv(Packet* p,Handler*)
{
}
Queue::recv这个函数调用 DropTail 规则将数据包加入队列,然后判断是否堵塞,如果没有则发送一个数据包,之前判断是认定这个包是否要被发送出去。这里也使用了target_->recv(),这里调用的是 DequeTrace::recv函数,将记录一个数据包出队列的记录。
4.6.
void LinkDelay::recv(Packet* p, Handler*h)
{
}
这个函数非常重要,这个是数据包最后离开这个节点的出口,由这个函数写一个事件加入schedule ,在一定的时间后调用。