python实现基于向量算法的的路由协议(RIP)

来源:互联网 发布:网络诋毁公司如何处理 编辑:程序博客网 时间:2024/05/02 02:10

1.实现的功能

  1. 多个路由器模拟;
  2. 路由器学习过程日志输出;
  3. 路由器路由信息输出(查看);
  4. 模拟网络故障;
  5. 多线程
  6. 图形化界面(显示、交互)。

2.算法的简单介绍

算法主要分为两块:GUI界面的实现和向量算法的实现
使用了多线程来让所有路由器一起工作,设置了每个路由器接受更新路由表的时间为1s。
由用户输入每个路由的初始路由表:路由名称和目的网络,距离默认为1(支持1-16的输入,大于16或小于1会提示错误),下一跳默认为空不填(支持填写)。

3.详细说明

一. GUI界面的实现:tkinter

实现了如下界面,包括了路由信息的输入,模拟路由器故障以及保存信息三个模块,使用了Button,Label,Entry,Text,messageBox,filedialog等控件。

界面:
界面

代码片段:

#GUI界面    root = Tk()    root.title('基于距离向量算法的路由协议的实现')    #左边的输入框    m_l = PanedWindow(showhandle = True, sashrelief = SUNKEN)    m_l.pack(fill = BOTH, expand = 1, padx = 10, pady = 10)    frame_l = LabelFrame(m_l, text = '输入路由信息:', font = 18, padx = 5, pady = 5)    frame_l.pack(padx = 10, pady = 10)    Label(frame_l, text = '路由器名称:', font = 16).grid(row = 0, column = 0, sticky = W, pady = 5)    Label(frame_l, text = '目的网络:', font = 16).grid(row = 1, column = 0, sticky = W, pady = 5)    Label(frame_l, text = '距离:', font = 16).grid(row = 2, column = 0, sticky = W, pady = 5)    Label(frame_l, text = '下一跳:', font = 16).grid(row = 3, column = 0, sticky = W, pady = 5)    e_name = Entry(frame_l, justify = CENTER)    e_name.grid(row = 0, column = 1)    e_net_tar = Entry(frame_l, justify = CENTER)    e_net_tar.grid(row = 1, column = 1)    e_distance = Entry(frame_l, justify = CENTER)    e_distance.grid(row = 2, column = 1)    e_next_ = Entry(frame_l, justify = CENTER)    e_next_.grid(row = 3, column = 1)    b_add = Button(frame_l, text = '添加', font = 18, command = lambda : add_callback(tables), padx = 20, pady = 5)    b_add.grid(row = 4, column = 0, pady = 5)    b_start = Button(frame_l, text = '更新', font = 18, command = lambda : update_callback(tables,table_new), padx = 20, pady = 5)    b_start.grid(row = 4, column = 1, pady = 5)    Label(frame_l, text = '', font = 16).grid(row = 5, column = 0, sticky = W, pady = 5)    Label(frame_l, text = '模拟网络故障:', font = 18).grid(row = 6, column = 0, sticky = W, pady = 5)    Label(frame_l, text = '故障路由器名称:', font = 16).grid(row = 7, column = 0, sticky = W, pady = 5)    e_name_w = Entry(frame_l, justify = CENTER)    e_name_w.grid(row = 7, column = 1)    b_w = Button(frame_l, text = '故障', font = 18, command = lambda : wrong_callback(tables), padx = 20, pady = 5)    b_w.grid(row = 8, column = 0, pady = 5, columnspan = 2)    Label(frame_l, text = '', font = 16).grid(row = 9, column = 0, sticky = W, pady = 5)    Label(frame_l, text = '保存信息:', font = 18).grid(row = 10, column = 0, sticky = W, pady = 5)    b_save_info = Button(frame_l, text = '保存路由表', font = 16, command = save_info_callback, padx = 10, pady = 5)    b_save_info.grid(row = 11, column = 0, pady = 5)    b_save_log = Button(frame_l, text = '保存日志', font = 16, command = save_log_callback, padx = 10, pady = 5)    b_save_log.grid(row = 11, column = 1, pady = 5)    #右边的信息和log    m_r = PanedWindow(orient = VERTICAL, showhandle = True, sashrelief = SUNKEN)    frame1 = LabelFrame(m_r, text = '路由表信息:', font = 16, padx = 5, pady = 5)    frame1.pack(padx = 10, pady = 10)    sb1 = Scrollbar(frame1)    sb1.pack(side = RIGHT, fill = Y)    t1 = Text(frame1, width = 60, height = 20, font = 16, state = DISABLED, yscrollcommand = sb1.set)    t1.pack(side = LEFT, fill = BOTH)    sb1.config(command = t1.yview)    m_r.add(frame1)    frame2 = LabelFrame(m_r, text = '日志信息:', font = 16, padx = 5, pady = 5)    frame2.pack(padx = 10, pady = 10)    sb2 = Scrollbar(frame2)    sb2.pack(side = RIGHT, fill = Y)    t2 = Text(frame2, width = 60, height = 15, font = 16, state = DISABLED, yscrollcommand = sb2.set)    t2.pack(side = LEFT, fill = BOTH)    sb2.config(command = t2.yview)    m_r.add(frame2)    m_l.add(frame_l)    m_l.add(m_r)    mainloop()

二.函数部分的实现

分为两个部分:实现该协议的函数和控件的callback函数

  1. 实现的主要函数的说明
    add函数:用户输入一个路由信息,便向路由表的集合中添加一组数据
    send函数:路由器每次发送自己的路由表的时候调用的函数
    update函数:路由器每次更新路由表调用的函数
    threads函数:为每个路由器的发送和更新路由表分配线程

  2. 控件的callback函数说明
    (1)add_callback函数:点击添加按钮时调用的函数,在函数中调用了 add函数
    (2)update_callback函数:用户点击更新按钮时调用的函数,在函数中调用了threads函数
    (3)wrong_callback函数:用户点击故障按钮时的调用的函数,在函数中将路由表的集合中的故障路由的表项去掉
    (4)save_info_callback&save_log_callbac函数:用户点击保存路由表/保存日志按钮时调用的函数,用于将信息以文本形式输出

三. 主要算法思想
update函数:这里实现了路由表的更新操作。假设每个路由器不知道相邻路由器的名称,只知道相邻的网络,则路由器发送路由表是以广播的形式,我用tables这个列表储存所有更新路由表的集合。
(1)首先需要找出相邻的路由,并从他们的路由表中得到更新表
先从自己的路由表中得到相邻网络的列表,存储在tar中。
(2)然后将tables中存储的每个更新表的项和tar中的进行比较,如果距离是1且目标网络在tar中,则将该路由的表存储在tables_n中。接着将tables_n中的项依次与本路由表的项进行比较(见下面的流程图) 。最后一步就是对故障的处理,如果有故障的路由,则将以故障路由为下一跳的所有项的距离设置为不可达(16)。
流程图
代码片段:

def update(table,tables,table_new):#更新自己的路由表        global str_update        table = copy.deepcopy(table)        tables = copy.deepcopy(tables)        #找出相邻的路由,并且得到更新表        tar = []        for i in table[1]:            if i[1]==1:                tar.append(i[0])                        tables.remove(table)        tables_n = copy.deepcopy(tables)        for each in tables:            flag = False            for t in each[1]:                if t[0] in tar and t[1] == 1:                    flag = True                    break                else:                    pass            if not flag:                tables_n.remove(each)        #开始更新                for each in tables_n:            str_update += '\n' + time_now() + '\n路由器%s收到了来自%s的更新表\n' % (table[0],each[0])        table_n = copy.deepcopy(table)        for each in tables_n:            n = each[0]            for tu in each[1]:                tu[1] += 1                if tu[1] == 17:                    tu[1] = 16                tu[2] = n                f = False                for t in table_n[1]:                      if t[0] == tu[0]:#如果目标网络相同                        if t[2] == n:#如果下一跳相同                            table_n[1][table_n[1].index(t)] = tu                            str_update += '\n' + time_now() + '\n路由器%s从路由器%s更新了表项:\n(目标地址:%s,距离:%d,下一跳:%s)——>\n(目标地址:%s,距离:%d,下一跳:%s)\n' % (table[0],n,t[0],t[1],t[2],tu[0],tu[1],tu[2])                        else:#目标网络不同                            if tu[1] < t[1]:                                table_n[1][table_n[1].index(t)] = tu                                str_update += '\n' + time_now() + '\n路由器%s从路由器%s更新了表项:\n(目标地址:%s,距离:%d,下一跳:%s)——>\n(目标地址:%s,距离:%d,下一跳:%s)\n' % (table[0],n,t[0],t[1],t[2],tu[0],tu[1],tu[2])                        f = True                        break                if not f:                    table_n[1].append(tu)                    str_update += '\n' + time_now() + '\n路由器%s从路由器%s添加了新的表项:\n(目标地址:%s,距离:%d,下一跳:%s)\n' % (table[0],n,tu[0],tu[1],tu[2])        #故障处理        for i in table_n[1]:            if i[2] == luyou_wrong:                i[1] = 16        lock.acquire()        table_new.append(table_n)        lock.release()

**

4.测试结果

**
1. 输入一条信息,并点击添加
添加
**
如果输入的距离不是1-16的数字(浮点数会舍去小数部分),会提示错误,比如输入字母或者大于16或小于1的数字
出错
出错

我们按照NET5-A-NET1-B-NET2-C-NET3-D-NET4-E-NET5的拓扑结构输入信息
输入拓扑

2.点击更新,分别看一下路由表信息和日志信息:
路由表信息会记录每一次的历史路由表信息日志
日志信息则会记录下每一次添加表项 ,更新表项的结果
更新
更新
更新

第一次更新之后,每个路由表变成了四项,我们再点击一次更新
再次更新
现在每个路由器变成了5项,且表项也都正确。

(3)我们让A路由器故障
A故障

再次点击更新
更新
我们看到下一跳为A的路由表项的距离都变成了16(不可达)

(4)我们点击保存路由表
保存
保存

我们保存到了桌面的’路由表.txt’中,找到并打开查看,发现路由信息已经保存进去了。
路由表

保存日志类似。
保存
日志

5.代码分享

**

from tkinter import *import threading,time,copy,os#全局变量lock = threading.Lock() #进程锁tables = []             #网络上的路由表的集合table_new = []          #更新后的路由表的集合                        #通过这两个表的交换赋值来更新luyou_wrong = '--'      #故障的表的名字(默认一次只能故障一个路由)#每次更新控件显示的字符串str_send = ''           #发送了自己路由表的信息str_update = ''         #更新了自己路由表的信息def time_now():#用于获得当前时间    return str(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))def main():#主函数    def add(data,tables):#由用户添加每个路由器的初始路由表        if tables:            flag = False            for i in range(len(tables)):                if data[0] == tables[i][0]:                    if [data[1],data[2],data[3]] in tables[i][1]:                        string = '路由器%s中已有该表项(目标地址:%s,距离:%d,下一跳:%s)' % (data[0],data[1],data[2],data[3])                    else:                        tables[i][1].append([data[1],data[2],data[3]])                                                string = '向路由器%s添加了表项(目标地址:%s,距离:%d,下一跳:%s)' % (data[0],data[1],data[2],data[3])                    flag = True                    break            if not flag:                tables.append([data[0],[[data[1],data[2],data[3]]]])                string = '添加了%s路由器\n向%s路由器添加了表项(目标地址:%s,距离:%d,下一跳:%s)' % (data[0],data[0],data[1],data[2],data[3])        else:            tables.append([data[0],[[data[1],data[2],data[3]]]])            string = '添加了%s路由器\n向%s路由器添加了表项(目标地址:%s,距离:%d,下一跳:%s)' % (data[0],data[0],data[1],data[2],data[3])        log(string)        return tables    def send(table):#向相邻网络发送自己的路由表        string = table[0] + '向相邻路由发送了自己的路由表 '        global str_send        str_send += time_now() + '\n' + string + '\n'    def update(table,tables,table_new):#更新自己的路由表        global str_update        table = copy.deepcopy(table)        tables = copy.deepcopy(tables)        #找出相邻的路由,并且得到更新表        tar = []        for i in table[1]:            if i[1]==1:                tar.append(i[0])                        tables.remove(table)        tables_n = copy.deepcopy(tables)        for each in tables:            flag = False            for t in each[1]:                if t[0] in tar and t[1] == 1:                    flag = True                    break                else:                    pass            if not flag:                tables_n.remove(each)        #开始更新                for each in tables_n:            str_update += '\n' + time_now() + '\n路由器%s收到了来自%s的更新表\n' % (table[0],each[0])        table_n = copy.deepcopy(table)        for each in tables_n:            n = each[0]            for tu in each[1]:                tu[1] += 1                if tu[1] == 17:                    tu[1] = 16                tu[2] = n                f = False                for t in table_n[1]:                      if t[0] == tu[0]:#如果目标网络相同                        if t[2] == n:#如果下一跳相同                            table_n[1][table_n[1].index(t)] = tu                            str_update += '\n' + time_now() + '\n路由器%s从路由器%s更新了表项:\n(目标地址:%s,距离:%d,下一跳:%s)——>\n(目标地址:%s,距离:%d,下一跳:%s)\n' % (table[0],n,t[0],t[1],t[2],tu[0],tu[1],tu[2])                        else:#下一跳不同                            if (tu[1] < t[1] and t[1] != 16) or tu[1] == 16:                                table_n[1][table_n[1].index(t)] = tu                                str_update += '\n' + time_now() + '\n路由器%s从路由器%s更新了表项:\n(目标地址:%s,距离:%d,下一跳:%s)——>\n(目标地址:%s,距离:%d,下一跳:%s)\n' % (table[0],n,t[0],t[1],t[2],tu[0],tu[1],tu[2])                        f = True                        break                if not f:                    table_n[1].append(tu)                    str_update += '\n' + time_now() + '\n路由器%s从路由器%s添加了新的表项:\n(目标地址:%s,距离:%d,下一跳:%s)\n' % (table[0],n,tu[0],tu[1],tu[2])        #故障处理        for i in table_n[1]:            if i[0] == luyou_wrong and i[1] == 1:                i[1] = 16        lock.acquire()        table_new.append(table_n)        lock.release()    def threads(tables,table_new):#多线程发送和更新,做到每一个路由器同时发送和更新(设置接收信息的时间为1s)        global str_send        global str_update        str_send = ''        str_update = ''        threadpool_1 = []        for each in tables:            th = threading.Thread(target= send, args= (each,))            threadpool_1.append(th)        for th in threadpool_1:            th.start()        for th in threadpool_1:            threading.Thread.join(th)        t2.config(state = NORMAL)        t2.insert(INSERT,'--------------------------------------------------\n发送情况:\n')        t2.insert(INSERT,str_send)        t2.see(END)        t2.config(state = DISABLED)        time.sleep(1)        threadpool_2 = []        for each in tables:            th = threading.Thread(target= update, args= (each,tables,table_new))            threadpool_2.append(th)        for th in threadpool_2:            th.start()        for th in threadpool_2:            threading.Thread.join(th)        t2.config(state = NORMAL)        t2.insert(INSERT,'--------------------------------------------------\n更新情况:\n')        t2.insert(INSERT,str_update)        t2.see(END)        t2.config(state = DISABLED)        return table_new    def log(string):#更新日志的函数        string = time_now() + '\n' + string + '\n'        t2.config(state = NORMAL)        t2.insert(INSERT,string)        t2.see(END)        t2.config(state = DISABLED)    def show_add(tables):#用户添加初始路由表时更新路由信息        string = ''        string += '--------------------------------------------------\n'        string += '更新时间:' + str(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())) + '\n'        for each in tables:            string += '路由器' + each[0]+':\n'            for i in range(len(each[1])):                string += '('+str(i+1) + ')' + ' 目标网络:'+ each[1][i][0] + '  距离:'+ str(each[1][i][1]) + '  下一跳:' + each[1][i][2] + '\n'                    t1.config(state = NORMAL)        t1.delete(1.0,END)          t1.insert(INSERT,string)        t1.see(END)        t1.config(state = DISABLED)    def show_up(tables):#每次更新路由表时更新路由信息        string = ''        string += '--------------------------------------------------\n'        string += '更新时间:' + str(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())) + '\n'        for each in tables:            string += '路由器' + each[0]+':\n'            for i in range(len(each[1])):                string += '('+str(i+1) + ')' + ' 目标网络:'+ each[1][i][0] + '  距离:'+ str(each[1][i][1]) + '  下一跳:' + each[1][i][2] + '\n'                    t1.config(state = NORMAL)        t1.insert(INSERT,string)        t1.see(END)        t1.config(state = DISABLED)    #控件的callback函数    def add_callback(tables):#添加按钮        distance = e_distance.get()        if distance.isdigit():            name = e_name.get()            net_tar = e_net_tar.get()             next_ = e_next_.get()            if name != '' and net_tar != '' and distance != '':                distance = int(distance)                if distance>=1 and distance<=16:                    data = [name,net_tar,distance,next_]                    tables = add(data,tables)                    show_add(tables)                else:                    messagebox.showinfo('警告','距离应当为1-16的整数!')            else:                messagebox.showinfo('警告','路由器名称,目标网络和距离不能为空!')        else:            messagebox.showinfo('警告','距离应当为1-16的整数!')    def update_callback(tables,table_new):#更新按钮        table_new.clear()        tables_n = threads(tables,table_new)        tables.clear()        tables.extend(tables_n)        show_up(tables)        return tables    def wrong_callback(tables):#故障按钮        global luyou_wrong        name_w = e_name_w.get()        if name_w == '':            messagebox.showinfo('警告','故障网络名称不能为空!')        else:            luyou_wrong = name_w            messagebox.showinfo('通知','网络%s已设置为故障!' % name_w)            t2.config(state = NORMAL)            t2.insert(INSERT, '网络%s故障!' % name_w)            t2.see(END)            t2.config(state = DISABLED)    def save_info_callback():#保存路由信息        if not t1.get(1.0,END).isspace():            file = filedialog.asksaveasfilename(defaultextension = '.txt', filetypes = [('txt,TXT','.txt'),('ALL','.*')])            os.chdir(os.path.dirname(file))            f = open(os.path.basename(file),'w')            f.write(t1.get(1.0,END))            f.close()            messagebox.showinfo('通知','路由表信息已保存在%s中!' % file)        else:            messagebox.showinfo('警告','路由表信息为空!')    def save_log_callback():#保存日志信息        if not t2.get(1.0,END).isspace():            file = filedialog.asksaveasfilename(defaultextension = '.txt', filetypes = [('txt,TXT','.txt'),('ALL','.*')])            os.chdir(os.path.dirname(file))            f = open(os.path.basename(file),'w')            f.write(t2.get(1.0,END))            f.close()            messagebox.showinfo('通知','日志信息已保存在%s中!' % file)        else:            messagebox.showinfo('警告','路由表信息为空!')    #GUI界面    root = Tk()    root.title('基于距离向量算法的路由协议的实现')    #左边的输入框    m_l = PanedWindow(showhandle = True, sashrelief = SUNKEN)    m_l.pack(fill = BOTH, expand = 1, padx = 10, pady = 10)    frame_l = LabelFrame(m_l, text = '输入路由信息:', font = 18, padx = 5, pady = 5)    frame_l.pack(padx = 10, pady = 10)    Label(frame_l, text = '路由器名称:', font = 16).grid(row = 0, column = 0, sticky = W, pady = 5)    Label(frame_l, text = '目的网络:', font = 16).grid(row = 1, column = 0, sticky = W, pady = 5)    Label(frame_l, text = '距离:', font = 16).grid(row = 2, column = 0, sticky = W, pady = 5)    Label(frame_l, text = '下一跳:', font = 16).grid(row = 3, column = 0, sticky = W, pady = 5)    e_name = Entry(frame_l, justify = CENTER)    e_name.grid(row = 0, column = 1)    e_net_tar = Entry(frame_l, justify = CENTER)    e_net_tar.grid(row = 1, column = 1)    e_distance = Entry(frame_l, justify = CENTER)    e_distance.grid(row = 2, column = 1)    e_next_ = Entry(frame_l, justify = CENTER)    e_next_.grid(row = 3, column = 1)    b_add = Button(frame_l, text = '添加', font = 18, command = lambda : add_callback(tables), padx = 20, pady = 5)    b_add.grid(row = 4, column = 0, pady = 5)    b_start = Button(frame_l, text = '更新', font = 18, command = lambda : update_callback(tables,table_new), padx = 20, pady = 5)    b_start.grid(row = 4, column = 1, pady = 5)    Label(frame_l, text = '', font = 16).grid(row = 5, column = 0, sticky = W, pady = 5)    Label(frame_l, text = '模拟网络故障:', font = 18).grid(row = 6, column = 0, sticky = W, pady = 5)    Label(frame_l, text = '故障网络名称:', font = 16).grid(row = 7, column = 0, sticky = W, pady = 5)    e_name_w = Entry(frame_l, justify = CENTER)    e_name_w.grid(row = 7, column = 1)    b_w = Button(frame_l, text = '故障', font = 18, command = lambda : wrong_callback(tables), padx = 20, pady = 5)    b_w.grid(row = 8, column = 0, pady = 5, columnspan = 2)    Label(frame_l, text = '', font = 16).grid(row = 9, column = 0, sticky = W, pady = 5)    Label(frame_l, text = '保存信息:', font = 18).grid(row = 10, column = 0, sticky = W, pady = 5)    b_save_info = Button(frame_l, text = '保存路由表', font = 16, command = save_info_callback, padx = 10, pady = 5)    b_save_info.grid(row = 11, column = 0, pady = 5)    b_save_log = Button(frame_l, text = '保存日志', font = 16, command = save_log_callback, padx = 10, pady = 5)    b_save_log.grid(row = 11, column = 1, pady = 5)    #右边的信息和log    m_r = PanedWindow(orient = VERTICAL, showhandle = True, sashrelief = SUNKEN)    frame1 = LabelFrame(m_r, text = '路由表信息:', font = 16, padx = 5, pady = 5)    frame1.pack(padx = 10, pady = 10)    sb1 = Scrollbar(frame1)    sb1.pack(side = RIGHT, fill = Y)    t1 = Text(frame1, width = 60, height = 20, font = 16, state = DISABLED, yscrollcommand = sb1.set)    t1.pack(side = LEFT, fill = BOTH)    sb1.config(command = t1.yview)    m_r.add(frame1)    frame2 = LabelFrame(m_r, text = '日志信息:', font = 16, padx = 5, pady = 5)    frame2.pack(padx = 10, pady = 10)    sb2 = Scrollbar(frame2)    sb2.pack(side = RIGHT, fill = Y)    t2 = Text(frame2, width = 60, height = 15, font = 16, state = DISABLED, yscrollcommand = sb2.set)    t2.pack(side = LEFT, fill = BOTH)    sb2.config(command = t2.yview)    m_r.add(frame2)    m_l.add(frame_l)    m_l.add(m_r)    mainloop()if __name__=='__main__':    main()
2 0