RIP协定是一种动静路由抉择协定。一个路由器,定时与和本身相邻的路由器替换路由表。路由器通过肯定的算法,依据对方发送的路由表更新本身的路由表。从而动静更新整个自治零碎内的所有路由器的路由表。

如果你只是为了实现学校的RIP协定试验但又不懂go语言,我能够简略的阐明一下,go外面的构造体相似其余语言中的类,只不过是只有成员变量的类。构造体的办法,相似类(对象)的办法。

这个试验的主函数中,我只设计了RIP调换路由表最初达到收敛的过程,并没有设计模仿某个路由器不可达的状况,当然也没有设计呈现“坏消息传得慢”的状况。

首先定义数据结构
RIP报文内的路由表项,定义了,去网段号为NetId的网段,须要转发给NextHop路由器,间隔为Distance。在这个试验中,我将路由器所在网段间隔设为1。下一跳路由器为-1时,代表间接交付,也就是指标网段在以后路由器所在网段中。

// RIP 路由表项type Info struct {    NetId    int    // 路由器所在网段间隔设为1    Distance int    // NextHop 为-1代表间接交付    NextHop  int}

路由表,蕴含一个路由表项类型的切片。

//路由表type RouterTable []Info

路由器。蕴含地址和路由表。这里因为只是个简略试验,我应用一个整形数来充当路由器地址。尽管IP地址自身也只是个32位无符号整数。

// 路由器type Router struct {    Addr  int    Table RouterTable}

定义路由器构造体的办法。

输入路由器信息办法。实现了String()接口,能够被内置的输入函数调用。

func (r Router) String() string {    return fmt.Sprint("路由器地址:", r.Addr, "\n路由表", r.Table)}

FindInfo办法,能够找到路由器本身的路由表内对应某个网段(netID)的路由表项的索引。如果以后路由器有这个网段的路由表项,返回其下标和True,如果没有,返回-1和False。具体有什么用,前面会说到。

func (r *Router) FindInfo(netId int) (int, bool) {    for i, info := range r.Table {        if netId == info.NetId {            return i, true        }    }    return -1, false}

重点:接管路由表办法。以后路由器会通过这个办法,接管其余路由器的地址(Addr)和路由表(recvTable),并批改本身的路由表(r.Table)

这个试验会应用多线程执行,所以为了防止多个线程同时批改本身路由表产生抵触,加了一个互斥锁(mux)

首先,路由器在收到其余路由器的路由表后,对其做预处理。将收到路由表内所有表项下一跳地址改为发送方路由器的地址,同时所有间隔加1。

而后,遍历收到的路由表(recvTable)。在每次遍历时,尝试找出和以后收到的路由表项(info)网段(info.NetId)雷同的(也就是对应的)本身路由表项的下标(existigInfoIndex)。

  1. 本身路由表内存不存在对应的表项?
    如果不存在,则将该表项退出本身路由表
    如果存在,执行2
  2. 如果存在本身对应表项,则查看其下一跳是否为发送方路由器的地址。
    如果是,则将本身对应表项信息更新为收到的表项信息。当然实际上这只会批改表项的间隔。
    批改的起因是因为收到的路由器表是最新的音讯,所有依照最新的为准。不论收到的间隔绝对本身已有的记录的是大还是小,所有按最新为准。
    如果不是,执行3
  3. 如果对应表项的下一跳路由器不是发送方路由器地址,则比对间隔。
    如果本身对应表项间隔大于收到的表项间隔,更新为收到的表项。
    如果不是,则不做任何事件,进入下一次遍历。

在真正的RIP协定中,还会存在发送本身路由表给指标路由器后,3分钟内收不到回复的路由表,就将本身表内下一跳为该指标路由器的表项的间隔设为16,即该路由器不可达。这里我的试验中没有模拟出这个。

批改实现本身路由表后,开释锁(mux.Unlock),输入以后路由表信息。

func (r *Router) Recv(recvRtrAddr int, recvTable RouterTable) {    var mux sync.Mutex    for i := range recvTable {        recvTable[i].NextHop = recvRtrAddr        recvTable[i].Distance += 1    }    mux.Lock()    for _, info := range recvTable {        existigInfoIndex, ok := r.FindInfo(info.NetId)        if !ok {            r.Table = append(r.Table, info)        } else {            if r.Table[existigInfoIndex].NextHop == recvRtrAddr {                r.Table[existigInfoIndex] = info            } else {                if r.Table[existigInfoIndex].Distance > info.Distance {                    r.Table[existigInfoIndex] = info                }            }        }    }    mux.Unlock()    fmt.Printf("\n%v\n", r)}

发送路由表办法。以后路由器将本身的地址和路由表,发送给指标路由器。
输出指标路由器对象指针(dstRtr),将本身的路由表复制一份,而后调用指标路由器对象的Recv()办法接管以后路由器的地址和路由表。

为什么要将本身的路由表显式复制一份呢,是因为go中的切片只是个援用,间接传递给函数批改,影响会传递到函数外。所以要进行深复制确保发送的只是一份正本。

func (r *Router) Send(dstRtr *Router) {    sendTable := make(RouterTable, len(r.Table))    copy(sendTable, r.Table)    dstRtr.Recv(r.Addr, sendTable)}

自动更新办法。这里有两个全局变量。ROUTER_LIST是一个蕴含所有路由器对象指针的切片。REACHABLE是一个批示两个路由器之间是否连贯(也就是是否相邻)的可达矩阵。这里的“可达”仅指物理上的连贯,不示意某个路由器坏了无奈回复RIP报文时的状况。
遍历所有路由器对象,如果是相邻的,且不是本身,这对其发送本身的路由表。

延时能够不加,在这个试验中因为没有设计超时设为不可达的状况,设置为无延时也是行的。

func (r *Router) AutoUpdate() {    for true {        time.Sleep(time.Second * 3)        for _, dstRtr := range ROUTER_LIST {            if dstRtr != r {                if REACHABLE[r.Addr][dstRtr.Addr] {                    r.Send(dstRtr)                }            }        }    }}

整份代码。
参考主函数第一行正文,试验中的网路拓扑构造是这样的。

N0 -R0- N1 -R1- N2 -R2- N3

N*代表网络号,R*代表路由器号(号和地址雷同)。-代表路由器和某几个网段连贯了。

通过多线程启动自动更新后,3个路由器之间会始终与相邻的路由器调换音讯,最初达到收敛,即趋于稳定。代码最初最初的wg.Wait()只是为了不让主线程退出。

package mainimport (    "fmt"    "sync"    "time")var REACHABLE [][]boolvar ROUTER_LIST []*Router// RIP 路由表项type Info struct {    NetId    int    // 路由器所在网段间隔设为1    Distance int    // NextHop 为-1代表间接交付    NextHop  int}// 路由器type Router struct {    Addr  int    Table RouterTable}//路由表type RouterTable []Infofunc (r *Router) FindInfo(netId int) (int, bool) {    for i, info := range r.Table {        if netId == info.NetId {            return i, true        }    }    return -1, false}func (r Router) String() string {    return fmt.Sprint("路由器地址:", r.Addr, "\n路由表", r.Table)}func (r *Router) Recv(recvRtrAddr int, recvTable RouterTable) {    var mux sync.Mutex    for i := range recvTable {        recvTable[i].NextHop = recvRtrAddr        recvTable[i].Distance += 1    }    mux.Lock()    for _, info := range recvTable {        existigInfoIndex, ok := r.FindInfo(info.NetId)        if !ok {            r.Table = append(r.Table, info)        } else {            if r.Table[existigInfoIndex].NextHop == recvRtrAddr {                r.Table[existigInfoIndex] = info            } else {                if r.Table[existigInfoIndex].Distance > info.Distance {                    r.Table[existigInfoIndex] = info                }            }        }    }    mux.Unlock()    fmt.Printf("\n%v\n", r)}func (r *Router) Send(dstRtr *Router) {    sendTable := make(RouterTable, len(r.Table))    copy(sendTable, r.Table)    dstRtr.Recv(r.Addr, sendTable)}func (r *Router) AutoUpdate() {    for true {        time.Sleep(time.Second * 0)        for _, dstRtr := range ROUTER_LIST {            if dstRtr != r {                if REACHABLE[r.Addr][dstRtr.Addr] {                    r.Send(dstRtr)                }            }        }    }}func main() {    // N0 -R0- N1 -R1- N2 -R2- N3    a := Router{0, []Info{}}    a.Table = append(a.Table, Info{0, 1, -1})    a.Table = append(a.Table, Info{1, 1, -1})    b := Router{1, []Info{}}    b.Table = append(b.Table, Info{1, 1, -1})    b.Table = append(b.Table, Info{2, 1, -1})    c := Router{2, []Info{}}    c.Table = append(c.Table, Info{2, 1, -1})    c.Table = append(c.Table, Info{3, 1, -1})    ROUTER_LIST = append(ROUTER_LIST, &a)    ROUTER_LIST = append(ROUTER_LIST, &b)    ROUTER_LIST = append(ROUTER_LIST, &c)    REACHABLE = [][]bool{        {true, true, false},        {true, true, true},        {false, true, true},    }    //a.Send(ROUTER_LIST[1])    go a.AutoUpdate()    go b.AutoUpdate()    go c.AutoUpdate()    var wg sync.WaitGroup    wg.Add(1)    wg.Wait()}

最初达到收敛时的输入是这样的

路由器地址:2路由表[{2 1 -1} {3 1 -1} {1 2 1} {0 3 1}]路由器地址:0路由表[{0 1 -1} {1 1 -1} {2 2 1} {3 3 1}]路由器地址:1路由表[{1 1 -1} {2 1 -1} {3 2 2} {0 2 0}]