DV算法(<<计算机网络:自顶向下方法>>第五章编程作业)
目录
一.算法原理
1.1目标
在一个网络中,实现一个 分布式 , 异步 , 迭代 的路由算法.即距离向量(Distance-Vecto算法
1.2算法的核心思想
考虑一个网络中的一个 节点 A ,与其直接相邻的邻居节点为 ,目标节点为 B.
路由算法为: A"询问"其所有邻居结点到B的距离.从中选择
A到邻居距离 + 邻居到B距离
最小的一个邻居.此邻居就是A选择的下一个结点,此值就是A到B的距离.
将上述语言描述为公式就是:
1.3算法的表格
假设网络中由N个节点,每一个结点需要维护两个表,一个用于存储自己到邻居节点的距离,一个用于存储DV表
以作业中的此图为例,结点0需要维护的第一个表为
这个表会在初始化中完成,我们认为每一个结点可以知道到其邻居结点的距离,其中的距离是结点到邻居的距离,但不一定是
最短距离(例如:结点0到结点3的最短距离为4)
对于无法到达的结点,将其值设为 999。
注意:除非线路的开销发生了改变,否则这个表是不会变的
结点需要维护的第二个表就是储存了“询问”结果的表(DV表),这个表的大小是 邻居数*4 ,由于实验中给的此表都是4*4的,有些只使用一部分即可
以结点0为例,此表为
这是DV算法收敛时表中的内容,按照实验给出的组织方式 Distance_table[a][b] 就代表了 结点b 到 结点a 的最小开销。
可以总结出一些这个表的规律:
#1.当算法收敛时,这个表是沿对角线对称的,a到b的距离就是b到a的距离(实验中的网络是无向的)
#2.表的行数一定4,因为所有的结点个数是4,但列数不固定,是结点的邻居数(自己也加在表里了)
把不是邻居结点的结点加入列中是没有意义的,因为就算此节点可以到达目的结点,自己也没有办法到达这个结点
比如说,想要得到从 结点0 到 结点3 的最小开销,查表可得值为 4
在结点0所维护的表中,如果说知道了第二三四列的值,就可以根据上面提到的公式把第一列给算出来了
那么,结点1,2,3是怎么知道自己到其他结点的最近距离的呢,答案就是不断的“询问”
1.4“更新”与“发布”
在初始化的时候,每个结点只知道自己到邻居结点的距离,就先将这个填入DV表中,其他的一律填999.以节点0为例,初始化时其DV表 为
每当结点更新了自己到其他结点的距离时,就要通知其邻居结点.发布其最新消息,在这里 结点0 通知 结点1 ,结点2 和 结点3.
通知的内容为自己结点到其他所有结点的距离(就是上图中的第一列,一个四个元素的数组)
当一个结点收到其邻居结点发来的信息时,就要更新此节点了
更新策略:其实就是算法的核心公式,举一个例子
这是结点1初始化时的表格,4 * 3 代表有2个邻居,在其初始化完后,会向其邻居(0 和 2)发送信息。
结点0(初始化是1.4的第一张图),收到了结点1的消息,消息的内容是 1,0,1,999.意思是
结点 1 到结点 0 的距离是 1
结点 1 到结点 1 的距离是 0
结点 1 到结点 2 的距离是 1
结点 1 到结点 3 的距离是 999(不可达)
结点0收到以后,先将其填到第二列,要注意到这时候收到的 结点1 到各个结点的距离不一定是最短的,最短距离有等算法收敛的时候才可以确定。
然后,就是更新结点 0 了,因为本来 结点0 到 结点 1 是不可达的,但现在 结点1 可达了,就有可能通过走 节点1 找到一条更短的路。
从 结点 0 经过 结点 1 到 结点0 的距离为: 1 + 1 = 2 < 0 不更新 0,0元素
从 结点 0 经过 结点 1 到 结点1 的距离为: 1 + 0 = 1 <= 1 不更新 1,0元素
从 结点 0 经过 结点 1 到 结点2 的距离为: 1 + 1 = 2 < 3 更新 2,0元素,初始化的时候是 3,现在是2
从 结点 0 经过 结点 1 到 结点3 的距离为: 1 + 999 = 不可达 < 7 不更新 3,0元素
#上面的例子中从 结点0 到 结点1 的距离不是通过DV表来的!!!是查上文第一个表来的(也就是记录了邻居结点距离的1*4表),如果没有这个表,在后续计算中结点到其邻居的距离信息就没有了。
比如结点0的DV表把结点0到结点2的距离更新为2了,就是走了 0 -> 1 -> 2这条线路,如果说后来 0->1 的开销变得很大,而结点又没有存储 0->3 距离,那么永远也不会走 0->3 这条线。这就体现了第一个表的重要性
更新后发布:每个结点其实只可以更改DV表中自己所在的那一列,其他列都依靠别的结点 “告诉” 自己,其他结点告诉自己是多少就是多少,不用且逻辑上也不能去更改。
当结点发现DV表中自己所在的那一列被更新了之后,就 “发布” 更新结果(上文所描述的四个元素的数组)给自己所有的邻居。
1.5注意事项
#1,每个节点只与邻居节点通信,且通信的内容仅限于自己到其他所有节点的开销(一个一维数组)
#2.每次通信都需要与所有的邻居通信
#3.只要一个节点到其他节点的距离发生改变,就要进行一次通信
#4.cost(x,v)不是当前节点DV表中存储的值,而是节点直接到其邻居节点的距离信息.
二.代码部分(以node0.c为例)
本实验用的测试框架使用一个 事件列表 来模拟路由器的各种行为。其给定的三个事件为
调用收发函数的时候还会将收发事件插入到事件链表中
2.1初始化部分
void rtinit0()
{
printf("init node0 ... \n");
init_for_distance_table(dt0.costs);
dt0.costs[0][THIS_NODE_ID] = 0;
dt0.costs[1][THIS_NODE_ID] = 1;
dt0.costs[2][THIS_NODE_ID] = 3;
dt0.costs[3][THIS_NODE_ID] = 7;
printdt0(&dt0);
int node0_init_mincost[4] = { 0 , 1 , 3 , 7};
node0_comm(node0_init_mincost);
printf("init node0 finish ... \n \n");
}
行为:#1.初始化node0的DV表
#2.调用node0_comm()函数 “发布” 更新消息
void node0_comm(int new_cost[4]){
struct rtpkt pkt_2_node1;
struct rtpkt pkt_2_node2;
struct rtpkt pkt_2_node3;
creatertpkt(&pkt_2_node1 , THIS_NODE_ID , 1, new_cost);
creatertpkt(&pkt_2_node2 , THIS_NODE_ID , 2, new_cost);
creatertpkt(&pkt_2_node3 , THIS_NODE_ID , 3, new_cost);
printf("node0_comm: create pkt finish \n");
tolayer2(pkt_2_node1);
tolayer2(pkt_2_node2);
tolayer2(pkt_2_node3);
}
node0_comm()函数就是给结点0的邻居 结点1 ,结点2 ,结点3发送更新,将这个操作写成一个函数,只要node0有更新就调用
2.2接收到消息
void rtupdate0(rcvdpkt)
struct rtpkt *rcvdpkt;
{
printf("node 0 recv pkt form node%d...\n",rcvdpkt -> sourceid);
int is_update = 0;
int recv_src_id = rcvdpkt -> sourceid;
for (int i = 0; i < 4 ; i++)
{
dt0.costs[i][recv_src_id] = rcvdpkt -> mincost[i];
int dis1 = node0_neighbor_distance[i];
int dis2 = node0_neighbor_distance[1] + dt0.costs[i][1];
int dis3 = node0_neighbor_distance[2] + dt0.costs[i][2];
int dis4 = node0_neighbor_distance[3] + dt0.costs[i][3];
int min_dis = find_min_dis(dis1,dis2,dis3,dis4);
if (dt0.costs[i][THIS_NODE_ID] != min_dis){ //节点有变化就要发送
is_update = 1;
}
dt0.costs[i][THIS_NODE_ID] = min_dis;
}
if (is_update) //如果节点有更新,那么就要通知所有的邻居节点
{
int new_cost[4] = { dt0.costs[0][THIS_NODE_ID] ,dt0.costs[1][THIS_NODE_ID]
,dt0.costs[2][THIS_NODE_ID] ,dt0.costs[3][THIS_NODE_ID]};
node0_comm(new_cost);
}
printdt0(&dt0);
printf("\n");
}
dis1到4分别为1.4中描述的那四个距离,返回其最小值,就算是一次更新了,如果结果没有变化,就不发布新消息,否则,就要通知全部邻居结点.node_neighbor_distance就是1.3中第一个表
2.3更改链路开销(只在node1.c和node2.c中)
linkhandler0(linkid, newcost)
int linkid, newcost;
{
printf("node0 recv link cost change: 0 -> %d = %d \n",linkid,newcost);
dt0.costs[linkid][THIS_NODE_ID] = newcost;
node0_neighbor_distance[1] = newcost;
int new_cost[4] = { dt0.costs[0][THIS_NODE_ID] ,dt0.costs[1][THIS_NODE_ID]
,dt0.costs[2][THIS_NODE_ID] ,dt0.costs[3][THIS_NODE_ID]};
node0_comm(new_cost);
}
行为:#1.更改DV表,更改node_neighbor_distance表
#2.发布更新
三.测试结果
第一步:初始化
第二步:初始化完成,算法收敛
此时的路由器状态为:
第三步:更改node0到node1之间的权值,直到算法收敛
此时路由器状态为
第四步:更改回初始状态,直到算法收敛
后记:在网上没有找到这个这个实验的博客,就自己写一个吧.有什么错误欢迎大家指出