要求
设计题目: 路由器查表过程模拟
设计要求: 编程模拟路由器查找路由表的过程,用(目的地址 掩码 下一跳)的IP路由表以及目的地址作为输入,为目的地址查找路由表,找出正确的下一跳并输出结果。
温馨提示:
①:本项目使用python完成
②:由于时间紧张,只做了基本的路由表查询,默认路由表和指定路由表没有做
③:本项目在老师要求的基础上做了进一步拓展,使用了数据库,并添加了自动构造路由表的功能,参考时请谨慎选择
④:构造路由表的时候我想使用BellmanFord算法,但是没有成功,只采用了BFS单元最短路径算法,各位如果有能帮我改正的,可以在router_sim_v1.py的代码里面,把def bellman_ford_min_dis(router_dic, net_dic):
方法里面的过程补全,希望大佬补完以后能够评论区发一下,感激不尽!😀
代码
router_sim_v1.py
'''
Autor:han1254
Email:1254763408@qq.com
'''
import pymysql
import tkinter
from tkinter import ttk
import queue
from net_matrix import *
from my_utils import *
class MainPart:
def __init__(self, db, cursor):
self.__db = db
self.__cursor = cursor
self.main_part = tkinter.Tk()
self.main_part.geometry("500x500")
self.main_part.configure(bg='yellow')
btn_add_net = tkinter.Button(master=self.main_part, text="添加Net", command=self.btn_add_net)
btn_add_net.pack()
btn_add_router = tkinter.Button(master=self.main_part, text="添加路由器", command=self.btn_add_router)
btn_add_router.pack()
btn_add_find_next = tkinter.Button(master=self.main_part, text="下一跳测试", command=self.btn_next_hop)
btn_add_find_next.pack()
btn_build_table = tkinter.Button(master=self.main_part, text="构建路由表", command=self.btn_build_table)
btn_build_table.pack()
btn_show_table = tkinter.Button(master=self.main_part, text="显示路由表", command=self.btn_show_table)
btn_show_table.pack()
btn_del_tables = tkinter.Button(master=self.main_part, text="清空路由表", command=self.btn_del_tables)
btn_del_tables.pack()
self.main_part.mainloop()
def btn_del_tables(self):
sql_del_tables(cur, db)
def btn_build_table(self):
build_table(cur, db)
def btn_show_table(self):
def delTv():
x = tv.get_children()
for item in x:
tv.delete(item)
def insert_data():
delTv()
router = box_select.get()
if router is None:
print("请选择路由器")
else:
table_data = sql_get_router_table_by_name(router, cur, db)
column_datas = []
for t in table_data:
temp = []
temp.append(t['r_name'])
temp.append(t['des_net'])
temp.append(t['mask_code'])
temp.append(t['cost'])
temp.append(t['nr_name'])
temp.append(t['next_ip'])
column_datas.append(temp)
for i, node in enumerate(column_datas):
tv.insert('', i, values=node)
window = tkinter.Toplevel(self.main_part)
r_datas = sql_get_all_routers(cur, db)
datas = []
for d in r_datas:
datas.append(d['router_name'])
box_select = ttk.Combobox(window, value=datas)
box_select.pack()
btn_confirm = tkinter.Button(window, text="显示", command=insert_data)
btn_confirm.pack()
# 实例化控件,设置表头样式和标题文本
columns = ("r_name", "des_net", "mask", "cost", "next_hop_name", "next_ip")
headers = ("路由器", "目的网络", "子网掩码", "距离", "下一跳", "下一跳ip")
widthes = (120, 120, 120, 120, 120, 120)
tv = ttk.Treeview(master=window, show="headings", columns=columns)
for (column, header, width) in zip(columns, headers, widthes):
tv.column(column, width=width, anchor="w")
tv.heading(column, text=header, anchor="w")
tv.pack()
def btn_next_hop(self):
def start_test():
router = box.get()
des = ip_input.get()
if router is not None:
table = sql_get_router_table_by_name(router, cur, db)
print(table)
flag = False
if des is not None:
print("正在遍历路由表...")
for entry in table:
print("当前条目:目的网络:" + entry['des_net'] + ', 掩码:' + entry['mask_code'] + '下一跳地址:' + entry[
'next_ip'])
if check_is_des(entry['des_net'], des, entry['mask_code']):
print("查找成功")
print(entry['nr_name'])
print(entry['next_ip'])
flag = True
break
if not flag:
print('查找失败')
else:
print('输入目的网络')
else:
print("选择路由器")
test_part = tkinter.Toplevel(self.main_part)
test_part.geometry('300x300')
input_label = tkinter.Label(master=test_part, text="选择中转路由")
r_datas = sql_get_all_routers(cur, db)
datas = []
for d in r_datas:
datas.append(d['router_name'])
box = ttk.Combobox(master=test_part, values=datas)
input_label.pack()
box.pack()
label_ip = tkinter.Label(master=test_part, text="输入目的地址")
ip_input = tkinter.Entry(master=test_part)
btn_confirm = tkinter.Button(master=test_part, text="开始测试", command=start_test)
label_ip.pack()
ip_input.pack()
btn_confirm.pack()
def btn_add_net(self):
def ret_command():
if check_ip_pack(net_mask_entry.get()):
print("是地址块形式")
add_net(self.__cursor, self.__db, net_mask_entry.get(), net_name_entry.get())
else:
print("形式错误")
add_net_part.destroy()
add_net_part = tkinter.Toplevel(self.main_part)
add_net_part.geometry('300x300')
label_net_mask = tkinter.Label(master=add_net_part, text="添加网络(地址块)")
net_mask_entry = tkinter.Entry(master=add_net_part)
label_net_name = tkinter.Label(master=add_net_part, text="名称")
net_name_entry = tkinter.Entry(master=add_net_part)
btn_confirm = tkinter.Button(master=add_net_part, text="确认", command=ret_command)
label_net_mask.pack()
net_mask_entry.pack()
label_net_name.pack()
net_name_entry.pack()
btn_confirm.pack()
def btn_add_router(self):
def add_router():
router_name = name_entry.get()
if (router_name == ''):
print("名字不能为空")
return
sql_add_router(self.__cursor, self.__db, router_name)
in1_ip = in1_ip_entry.get()
in1_pack = in1_ip_pack_entry.get()
in2_ip = in2_ip_entry.get()
in2_pack = in2_ip_pack_entry.get()
in3_ip = in3_ip_entry.get()
in3_pack = in3_ip_pack_entry.get()
in4_ip = in4_ip_entry.get()
in4_pack = in4_ip_pack_entry.get()
if in1_ip != '' and in1_pack != '':
sql_add_router_int(self.__cursor, self.__db, router_name, in1_ip, in1_pack, 1)
if in2_ip != '' and in2_pack != '':
sql_add_router_int(self.__cursor, self.__db, router_name, in2_ip, in2_pack, 2)
if in3_ip != '' and in3_pack != '':
sql_add_router_int(self.__cursor, self.__db, router_name, in3_ip, in3_pack, 3)
if in4_ip != '' and in4_pack != '':
sql_add_router_int(self.__cursor, self.__db, router_name, in4_ip, in4_pack, 4)
# add_router_part.destroy()
add_router_part = tkinter.Toplevel(self.main_part)
add_router_part.geometry("300x300")
label_name = tkinter.Label(master=add_router_part, text="路由器名:")
name_entry = tkinter.Entry(master=add_router_part)
label_name.grid(row=0, column=0)
name_entry.grid(row=0, column=1)
label_in1_ip = tkinter.Label(master=add_router_part, text="接口1的ip:")
in1_ip_entry = tkinter.Entry(master=add_router_part)
label_in1_ip_pack = tkinter.Label(master=add_router_part, text="接口1连接的网络:")
in1_ip_pack_entry = tkinter.Entry(master=add_router_part)
label_in2_ip = tkinter.Label(master=add_router_part, text="接口2的ip:")
in2_ip_entry = tkinter.Entry(master=add_router_part)
label_in2_ip_pack = tkinter.Label(master=add_router_part, text="接口2连接的网络:")
in2_ip_pack_entry = tkinter.Entry(master=add_router_part)
label_in3_ip = tkinter.Label(master=add_router_part, text="接口3的ip:")
in3_ip_entry = tkinter.Entry(master=add_router_part)
label_in3_ip_pack = tkinter.Label(master=add_router_part, text="接口3连接的网络:")
in3_ip_pack_entry = tkinter.Entry(master=add_router_part)
label_in4_ip = tkinter.Label(master=add_router_part, text="接口4的ip:")
in4_ip_entry = tkinter.Entry(master=add_router_part)
label_in4_ip_pack = tkinter.Label(master=add_router_part, text="接口4连接的网络:")
in4_ip_pack_entry = tkinter.Entry(master=add_router_part)
btn_confirm = tkinter.Button(master=add_router_part, text="确认添加", command=add_router)
label_in1_ip.grid(row=1, column=0)
label_in1_ip_pack.grid(row=2, column=0)
in1_ip_entry.grid(row=1, column=1)
in1_ip_pack_entry.grid(row=2, column=1)
label_in2_ip.grid(row=3, column=0)
label_in2_ip_pack.grid(row=4, column=0)
in2_ip_entry.grid(row=3, column=1)
in2_ip_pack_entry.grid(row=4, column=1)
label_in3_ip.grid(row=5, column=0)
label_in3_ip_pack.grid(row=6, column=0)
in3_ip_entry.grid(row=5, column=1)
in3_ip_pack_entry.grid(row=6, column=1)
label_in4_ip.grid(row=7, column=0)
label_in4_ip_pack.grid(row=8, column=0)
in4_ip_entry.grid(row=7, column=1)
in4_ip_pack_entry.grid(row=8, column=1)
btn_confirm.grid(row=9, column=0)
def get_nets(cursor, db):
datas = None
try:
q = "SELECT * FROM net"
cur.execute(query=q)
datas = cur.fetchall()
print(datas)
except:
print("查询出错")
finally:
return datas
def sql_get_router_table_by_name(name, cursor, db):
datas = {}
try:
sql = 'SELECT * FROM router_table WHERE r_name=%s'
cursor.execute(sql, name)
datas = cursor.fetchall()
except Exception as e:
print(e)
finally:
return datas
def sql_get_router_table(id, cursor, db):
datas = {}
try:
sql = 'SELECT * FROM router_table WHERE router_id=%s'
cursor.execute(sql, id)
datas = cursor.fetchall()
except Exception as e:
print(e)
finally:
return datas
def sql_get_all_routers(cursor, db):
try:
get_all_sql = "SELECT * FROM router"
cursor.execute(get_all_sql)
return cur.fetchall()
except Exception as e:
print(e)
def sql_del_tables(cursor, db):
del_sql = 'TRUNCATE TABLE router_table'
try:
cursor.execute(del_sql)
db.commit()
except Exception as e:
print(e)
def sql_save_tables(router_dic, tables, cursor, db):
del_sql = 'TRUNCATE TABLE router_table'
cursor.execute(del_sql)
# self.id = router_id
# self.r_name = r_name
# self.des = des
# self.mask = sub_mask
# self.cost = cost
# self.next_id = next_hop
# INSERT
# INTO
# router_table(des_net, mask_code, router_id, r_name, cost, next_hop, nr_name)
# VALUES('130.10.0.0/16', '255.255.0.0', 3, 'R2', 1, 'no', 'no');
insert_tables_sql = 'INSERT INTO router_table (des_net,mask_code, router_id, r_name, cost, next_hop, nr_name, next_ip) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)'
try:
for entry in tables:
next_id = ''
next_name = 'no'
next_ip = 'no'
if entry.next_id is None:
router = router_dic[entry.id]
dic = router.router_dic
for i in range(4): ##遍历四个接口
int_net = 'int' + str(i + 1) + '_net'
if dic[int_net] == entry.des:
next_id = int_net
next_name = int_net
break
else:
next_id = entry.next_id
next_name = router_dic[entry.next_id].name
next_ip = entry.next_ip
cursor.execute(insert_tables_sql,
(entry.des, entry.mask, entry.id, entry.r_name,
entry.cost, next_id, next_name, next_ip))
db.commit()
except Exception as e:
print(e)
def add_net(cursor, db, net_mask, net_name):
try:
select_query = 'SELECT * FROM net WHERE net_name=%s'
temp_net = cursor.execute(select_query, net_name)
if (temp_net == 0):
add_net_query = """INSERT INTO `net` (`net_ip_mask`, `net_name`) VALUES (%s, %s)"""
cursor.execute(add_net_query, (net_mask, net_name))
db.commit()
else:
print("存在同名网络")
except:
print("添加网络失败")
db.close()
def sql_add_router(cursor, db, name):
router_query = 'SELECT * FROM router WHERE router_name=%s'
try:
temp_router = cursor.execute(router_query, name)
if temp_router != 0:
print("存在该路由器")
return
except:
print("查询失败")
add_router_insert = 'INSERT INTO router (router_name) VALUES (%s)'
try:
cursor.execute(add_router_insert, name)
db.commit()
except:
print("添加失败")
def sql_add_router_int(cursor, db, name, ip, pack, interface):
in_net = ''
in_ip = ''
if interface == 1:
in_net = 'int1_net'
in_ip = 'int1_ip'
elif interface == 2:
in_net = 'int2_net'
in_ip = 'int2_ip'
elif interface == 3:
in_net = 'int3_net'
in_ip = 'int3_ip'
else:
in_net = 'int4_net'
in_ip = 'int4_ip'
inf_alter = 'UPDATE router SET ' + in_net + '=%s,' + in_ip + '=%s WHERE router_name=%s'
try:
cursor.execute(inf_alter, (pack, ip, name))
db.commit()
except Exception as e:
print("路由器更改失败")
print(e)
print("修改成功!")
def build_table(cur, db):
sql_router_query = "SELECT * FROM router"
sql_net_query = "SELECT * FROM net"
cur.execute(sql_router_query)
routers = cur.fetchall()
cur.execute(sql_net_query)
nets = cur.fetchall()
print(routers)
print(nets)
router_dic, net_dic = build_nodes_list(routers, nets)
##对所有的网络结点进行广度优先遍历,就可以计算出最短路径
tables = bfs_min_distance(router_dic, net_dic)
# bellman_ford_min_dis(router_dic, net_dic)
sql_save_tables(router_dic, tables, cur, db)
print('Very OK')
def bellman_ford_min_dis(router_dic, net_dic):
'''
废弃!!!! discarded
:param router_dic: ['router_id':Router]
:param net_dic: ['net':Net]
:return:
'''
tables = []
union_list = []
for r in router_dic:
union_list.append(router_dic[r])
for n in net_dic:
union_list.append(net_dic[n])
for r in router_dic:
net_pre = {}
router_pre = {}
min_dis_to_net = {}
min_dis_to_router = {}
for n in router_dic:
min_dis_to_router[n] = 16
for n in net_dic:
min_dis_to_net[n] = 16
min_dis_to_router[r] = 0
next_hop = {}
for i in range(len(union_list) - 1): # 运行n(V) - 1次
for node in union_list:
arc = node.next
while arc is not None:
if arc.from_type is NodeType.ROUTER:
from_dis = min_dis_to_net[arc.from_net]
else:
from_dis = min_dis_to_router[arc.from_router]
if arc.to_type is NodeType.NET:
if from_dis + 1 < min_dis_to_net[arc.to_net]:
net_dic[arc.to_net].set_from_router(router_dic[arc.from_router], arc.from_net)
min_dis_to_net[arc.to_net] = from_dis + 1
net_pre[arc.to_net] = router_dic[arc.from_router]
else:
if from_dis + 1 < min_dis_to_router[arc.to_router]:
min_dis_to_router[arc.to_router] = from_dis + 1
arc = arc.next
def bfs_min_distance(router_dic, net_dic):
'''
soul of the whole code
:param router_dic:
:param net_dic:
:return:
'''
tables = []
for net in net_dic:
net_visited = {} # 记录net结点是否遍历过
for n in net_dic:
net_visited[n] = False
net_dic[n].set_from_router(None, None) # 每次循环的时候要把from_router属性置空
router_visited = {} # 记录router结点是否遍历过
for r in router_dic:
router_visited[r] = False
cost_to_net = {} # 记录每个结点到网络结点的距离
for n in net_dic:
cost_to_net[n] = 1
cost_to_router = {}
for r in router_dic:
cost_to_router[r] = 1
net_node = net_dic[net]
net_visited[net] = True
t_queue = queue.Queue()
t_queue.put(net_node)
while not t_queue.empty():
node = t_queue.get()
arc = node.next
while arc is not None:
if arc.to_type is NodeType.NET and not net_visited[arc.to_net]:
net_visited[arc.to_net] = True
t_queue.put(net_dic[arc.to_net])
cost_to_net[arc.to_net] = cost_to_router[arc.from_router] + 1
net_dic[arc.to_net].set_from_router(node, arc.from_net)
elif arc.to_type is NodeType.ROUTER and not router_visited[arc.to_router]:
router_visited[arc.to_router] = True
t_queue.put(router_dic[arc.to_router])
cost_to_router[arc.to_router] = cost_to_net[arc.from_net]
# pre = None if pre_router_queue.empty() else pre_router_queue.get()
pre_r = None if net_dic[arc.from_net] is None else net_dic[arc.from_net].from_router
if pre_r is not None and pre_r.id is router_dic[arc.to_router].id:
pre_r = None
pre_i = None if pre_r is None else net_dic[arc.from_net].from_ip
entry = TableEntry(router_id=arc.to_router,
des=net, sub_mask=get_mask(net),
cost=cost_to_router[arc.to_router],
next_hop=None if pre_r is None else pre_r.id,
r_name=router_dic[arc.to_router].name,
next_ip=pre_i)
tables.append(entry)
arc = arc.next
return tables
def build_nodes_list(routers, nets):
routerNodes = []
for router in routers:
rNode = Router(router['router_id'], router['router_name'], router)
routerNodes.append(rNode)
net_dic = {}
for net in nets:
nNode = Net(net['net_ip_mask'], net['net_name'])
net_dic[net['net_ip_mask']] = nNode
router_dic = {}
for node in routerNodes:
router_dic[node.id] = node
temp_node = node
r = node.router_dic
for i in range(4): # 遍历四个接口
int_net = 'int' + str(i + 1) + '_net'
int_ip = 'int' + str(i + 1) + '_ip'
if r[int_net] is not None and r[int_ip] is not None:
net_node = net_dic[r[int_net]]
arc = Arc(NodeType.ROUTER, NodeType.NET, r[int_ip], r[int_net])
arc.set_from_router_id(node.id)
temp_node.next = arc
temp_node = temp_node.next
while net_node.next is not None:
net_node = net_node.next
arc = Arc(NodeType.NET, NodeType.ROUTER, r[int_net], r[int_ip])
arc.set_to_router_id(node.id) # 设置目的路由器id,路由器有多个接口,只能通过id才能快速找到(其实通过数据库查找也可以)
net_node.next = arc
return router_dic, net_dic
if __name__ == '__main__':
db = pymysql.connect(host='localhost', user='han1254', passwd='8282481aa', database='rip_copy')
cur = db.cursor(pymysql.cursors.DictCursor)
MainPart(db, cur)
db.close()
net_matrix.py
from enum import Enum
from my_utils import *
class NodeType(Enum):
ROUTER = 1
NET = 2
class Router:
def __init__(self, id, router_name, router_dic):
self.id = id
self.name = router_name
self.router_dic = router_dic
self.next = None # list of class Arc
class Net:
def __init__(self, net, net_name):
self.net = net
self.mask = get_mask(net)
self.name = net_name
self.next = None # list of class Arc
self.from_router = None
self.from_ip = None
def set_from_router(self, router, ip):
self.from_router = router
self.from_ip = ip
class Arc():
def __init__(self, from_type, to_type, from_net, to_net):
self.from_type = from_type
self.to_type = to_type
self.from_net = from_net
self.to_net = to_net
self.next = None
self.from_router = -1 # 默认是-1
self.to_router = -1 # 默认是-1
def set_to_router_id(self, router_id):
"""
如果指向的是一个Router的话,我需要知道指向的是哪一个
:param router_id:
:return:
"""
self.to_router = router_id
def set_from_router_id(self, router_id):
"""
如果来自一个Router,我需要知道来自哪个router
:param router_id:
:return:
"""
self.from_router = router_id
class TableEntry():
def __init__(self, router_id, r_name, des, sub_mask, cost, next_hop, next_ip):
self.id = router_id
self.r_name = r_name
self.des = des
self.mask = sub_mask
self.cost = cost
self.next_id = next_hop
self.next_ip = next_ip
my_utils.py
import re
def exchange_maskint(mask_int):
"""
通过掩码位数获得子网掩码
:param mask_int:
:return:
"""
bin_arr = ['0' for i in range(32)]
for i in range(mask_int):
bin_arr[i] = '1'
tmpmask = [''.join(bin_arr[i * 8:i * 8 + 8]) for i in range(4)]
tmpmask = [str(int(tmpstr, 2)) for tmpstr in tmpmask]
return '.'.join(tmpmask)
def get_mask(cidr):
"""
获得掩码
:param cidr:
:return:
"""
mask = cidr.split("/")[1]
return exchange_maskint(int(mask))
def check_is_des(des_net, des_ip, mask):
"""
检测目的ip是否为当前路由表中一项的目的网络
:param des_net: 路由器的一个目的网络
:param des_ip: 目的ip地址或者目的网络
:param mask: 子网掩码
:return:
"""
mask_list = mask.split('.')
source_list = des_ip.split('.')
result_list = [str(int(mask_list[i]) & int(source_list[i])) for i in range(4)]
des_list = des_net.split('/')[0].split('.')
for i in range(4):
if des_list[i] != result_list[i]:
return False
return True
def check_mask(mask_str):
"""
检查是否为掩码形式
:param mask_str:
:return:
"""
pattern = '^((128|192)|2(24|4[08]|5[245]))(\.(0|(128|192)|2((24)|(4[08])|(5[245])))){3}$'
res = re.match(pattern, mask_str)
if not res:
return False
return True
def check_ip_pack(ip_str):
"""
校验地址块
:param ip_str:
:return:
"""
pattern = '^((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}/([0-2]?\d)|30$'
res = re.match(pattern, ip_str)
if not res:
return False
return True
一些截图
数据库设计ER图
注意先构建路由表,然后再显示
正在遍历路由表...
当前条目:目的网络:130.10.0.0/16, 掩码:255.255.0.0下一跳地址:no
当前条目:目的网络:130.11.0.0/16, 掩码:255.255.0.0下一跳地址:no
当前条目:目的网络:195.2.4.0/24, 掩码:255.255.255.0下一跳地址:130.10.0.1
当前条目:目的网络:195.2.5.0/24, 掩码:255.255.255.0下一跳地址:130.10.0.1
当前条目:目的网络:195.2.6.0/24, 掩码:255.255.255.0下一跳地址:130.10.0.1
当前条目:目的网络:205.5.5.0/24, 掩码:255.255.255.0下一跳地址:130.11.0.1
当前条目:目的网络:205.5.6.0/24, 掩码:255.255.255.0下一跳地址:130.11.0.1
查找成功
R4
130.11.0.1
重点
废话不多说了,直接github见:传送门