基于Simpy的通信网络仿真工具(二):多主机、端口互联实现数据包转发与交换机仿真
基于Simpy/Python的通信网络仿真工具(一):数据包生成,转发和接收
Host模块
在主机(Host)中具有数据包生成器(Pg)和接收器(Ps),实现代码如下
class Host(object):
"""
功能: 实现数据包产生、发出和接收
参数
-------
address : 地址
dist : 时隙大小
dst_adrees : 目的地址列表
size_list : 包大小列表
interval : 包产生间隔时间
rec_arrivals: 包接收间隔时间
absolute_arrivals: 包接收时间
debug: 日志打印
"""
def __init__(self, env, address, dst_address, interval=None, rec_arrivals=False, absolute_arrivals=False, debug=False) -> None:
self.env = env
self.address = address
self.pg = PacketGenerator(env, src=address, dst_list=dst_address, interval=interval)
self.ps = PacketSink(
env, host=address, rec_arrivals=rec_arrivals, absolute_arrivals=absolute_arrivals, debug=debug)
# 设置包的大小信息
def setPktSize(self, pkt_size_list):
"""
pkt_size_list : size列表
"""
self.size_list = pkt_size_list
# 连接(out)目标交换机
def setLink(self, target):
self.pg.out = target.in_port
Port模块
一个端口(Port)需要同时具备接收、转发和发出功能,代码如下
class Port(object):
"""
功能: 接收和转发全功能端口,实现端口的接收和转发
参数
------
env: 运行环境
port_id: 端口的id
trans_list: 内部端口的连接列表, A_in --> B_out, C_out
table: 转发规则表, src-dst : port_out
valid_time: 流表有效时间
send_rate: out端口的传输速率
queue_limit: 队列最大长度
is_limit: 是否丢包
debug: 输出执行信息
"""
def __init__(self, env, port_id, trans_list, table=None, valid_time=None,
send_rate=0, queue_limit=None, is_limit=True, debug=False) -> None:
self.env = env
self.switch_id = None
self.port_id = port_id
self.queue = [] # 端口队列记录
self.drop = [] # 端口丢包记录
self.in_port = SwitchPortIn(env=env, trans_list=trans_list, valid_time=valid_time,
table=table, debug=debug)
self.out_port = SwitchPortOut(env=env, address=None, rate=send_rate, qlimit=queue_limit,
limit_bytes=is_limit, debug=debug)
# 设置转发表(判断包应该转发到哪个port的out)
def setTable(self, table):
self.in_port.table = table
# 设置out端口的连接对象
def setOutObj(self, out):
self.out_port.out = out
# 设置in端口的连接对象
def setTransPort(self, target_list):
for i in range(len(target_list)):
self.in_port.out[i] = target_list[i].out_port
# 获取out端口的队列
def getQueue(self):
return self.out_port.queue
# 输出out端口的丢包情况
def getOutDrop(self):
return self.out_port.out_drop
def __repr__(self):
return "switch_address :{}, port_id :{}".format(self.switch_id, self.port_id)
三主机、端口传输仿真
在数据包数据包生成,转发和接收和基础上,本部分根据以下拓扑实现,数据转发仿真
# Port仿真, 三port, 三host
import simpy
import random
UNTIL_TIME = 10 # 仿真时间
def constArrival():
return 0.8 # time interval
def getTransList1():
return [2, 3]
def getTransList2():
return [1, 3]
def getTransList3():
return [1, 2]
# 生成转发规则
def getTable1():
return {"H1-H2": 2, "H1-H3": 3}
def getTable2():
return {"H2-H1": 1, "H2-H3": 3}
def getTable3():
return {"H3-H1": 1, "H3-H2": 2}
if __name__=='__main__':
port_rate = 1200.0
env = simpy.Environment() # Create the SimPy environment
h1 = Host(env, "H1", dst_address=["H2", "H3"], interval=constArrival, rec_arrivals=True, debug=False)
h2 = Host(env, "H2", dst_address=["H1", "H3"], interval=constArrival, rec_arrivals=True, debug=False)
h3 = Host(env, "H3", dst_address=["H1", "H2"], interval=constArrival, rec_arrivals=True, debug=False)
pt1 = Port(env=env, port_id = 1, trans_list=getTransList1(), table=getTable1(),
send_rate=port_rate, queue_limit=1001, debug=False)
pt2 = Port(env=env, port_id = 2, trans_list=getTransList2(), table=getTable2(),
send_rate=port_rate, queue_limit=1001, debug=False) # rate 是波特率
pt3 = Port(env=env, port_id = 3, trans_list=getTransList3(), table=getTable3(),
send_rate=port_rate, queue_limit=1001, debug=False) # rate 是波特率
h1.pg.out = pt1.in_port
pt1.setTransPort(target_list=[pt2, pt3])
pt2.setOutObj(h2.ps)
h2.pg.out = pt2.in_port
pt2.setTransPort(target_list=[pt1, pt3])
pt1.setOutObj(h1.ps)
h3.pg.out = pt3.in_port
pt3.setTransPort(target_list=[pt1, pt2])
pt3.setOutObj(h3.ps)
env.run(until=UNTIL_TIME)
print("received: {}, queuing: {}, out dropped {}, sent {}".format(
h1.ps.packets_rec + h2.ps.packets_rec + h3.ps.packets_rec,
len(pt1.out_port.queue)+ len(pt2.out_port.queue) + len(pt3.out_port.queue),
len(pt1.getOutDrop())+ len(pt2.getOutDrop()) + len(pt3.getOutDrop()),
h1.pg.packets_sent + h2.pg.packets_sent + h3.pg.packets_sent))
print("h1 received ids: {}".format(h1.ps.arrivals_id))
print("h2 received ids: {}".format(h2.ps.arrivals_id))
print("h3 received ids: {}".format(h3.ps.arrivals_id))
print("pt1 queue ids : {}".format([pkt.id for pkt in pt1.out_port.queue]))
print("pt2 queue ids: {}".format([pkt.id for pkt in pt2.out_port.queue]))
print("pt3 queue ids: {}".format([pkt.id for pkt in pt3.out_port.queue]))
print("pt1 out drop ids: {}".format([pkt.id for pkt in pt1.out_port.out_drop]))
print("pt2 out drop ids: {}".format([pkt.id for pkt in pt2.out_port.out_drop]))
print("pt3 out drop ids: {}".format([pkt.id for pkt in pt3.out_port.out_drop]))
结果如下:
received: 42, queuing: 26, out dropped 4, sent 72
h1 received ids: ['H2-H1-1', 'H3-H1-1', 'H2-H1-3', 'H3-H1-3', 'H2-H1-5', 'H3-H1-5', 'H2-H1-7', 'H3-H1-7', 'H2-H1-9', 'H3-H1-9', 'H2-H1-11', 'H3-H1-11', 'H2-H1-13', 'H3-H1-13', 'H2-H1-15']
h2 received ids: ['H1-H2-1', 'H3-H2-2', 'H1-H2-3', 'H3-H2-4', 'H1-H2-5', 'H3-H2-6', 'H1-H2-7', 'H3-H2-8', 'H1-H2-9', 'H3-H2-10', 'H1-H2-11', 'H3-H2-12', 'H1-H2-13', 'H3-H2-14', 'H1-H2-15']
h3 received ids: ['H1-H3-2', 'H2-H3-2', 'H1-H3-4', 'H2-H3-4', 'H1-H3-6', 'H2-H3-6', 'H1-H3-8', 'H2-H3-8', 'H1-H3-10', 'H2-H3-10', 'H1-H3-12', 'H2-H3-12']
pt1 queue ids : ['H3-H1-15', 'H2-H1-17', 'H3-H1-17', 'H2-H1-19', 'H3-H1-19', 'H2-H1-21', 'H3-H1-21', 'H2-H1-23', 'H3-H1-23']
pt2 queue ids: ['H3-H2-16', 'H1-H2-17', 'H3-H2-18', 'H1-H2-19', 'H3-H2-20', 'H1-H2-21', 'H3-H2-22', 'H1-H2-23', 'H3-H2-24']
pt3 queue ids: ['H1-H3-14', 'H2-H3-14', 'H1-H3-16', 'H2-H3-16', 'H1-H3-18', 'H1-H3-20', 'H1-H3-22', 'H2-H3-22']
pt1 out drop ids: []
pt2 out drop ids: []
pt3 out drop ids: ['H2-H3-18', 'H2-H3-20', 'H1-H3-24', 'H2-H3-24']
通过设置数据包产生间隔时间,影响网络的拥塞程度。
def constArrival():
return 0.6 # time interval
运行结果
received: 44, queuing: 37, out dropped 15, sent 96
h1 received ids: ['H2-H1-1', 'H3-H1-1', 'H2-H1-3', 'H3-H1-3', 'H2-H1-5', 'H3-H1-5', 'H2-H1-7', 'H3-H1-7', 'H2-H1-9', 'H3-H1-9', 'H2-H1-11', 'H3-H1-11', 'H2-H1-13', 'H3-H1-13', 'H2-H1-15']
h2 received ids: ['H1-H2-1', 'H3-H2-2', 'H1-H2-3', 'H3-H2-4', 'H1-H2-5', 'H3-H2-6', 'H1-H2-7', 'H3-H2-8', 'H1-H2-9', 'H3-H2-10', 'H1-H2-11', 'H3-H2-12', 'H1-H2-13', 'H3-H2-14']
h3 received ids: ['H1-H3-2', 'H2-H3-2', 'H1-H3-4', 'H2-H3-4', 'H1-H3-6', 'H2-H3-6', 'H1-H3-8', 'H2-H3-8', 'H1-H3-10', 'H2-H3-10', 'H1-H3-12', 'H2-H3-12', 'H1-H3-14', 'H2-H3-14', 'H1-H3-16']
pt1 queue ids : ['H3-H1-15', 'H2-H1-17', 'H3-H1-17', 'H2-H1-19', 'H2-H1-21', 'H3-H1-21', 'H2-H1-23', 'H3-H1-23', 'H2-H1-25', 'H3-H1-25', 'H2-H1-29', 'H3-H1-29']
pt2 queue ids: ['H1-H2-15', 'H3-H2-16', 'H1-H2-17', 'H3-H2-18', 'H1-H2-19', 'H1-H2-21', 'H3-H2-24', 'H3-H2-26', 'H1-H2-27', 'H3-H2-28', 'H1-H2-29', 'H3-H2-30', 'H1-H2-31', 'H3-H2-32']
pt3 queue ids: ['H2-H3-16', 'H1-H3-18', 'H1-H3-20', 'H2-H3-20', 'H1-H3-22', 'H1-H3-24', 'H1-H3-26', 'H1-H3-28', 'H2-H3-28', 'H1-H3-30', 'H2-H3-30']
pt1 out drop ids: ['H3-H1-19', 'H2-H1-27', 'H3-H1-27', 'H2-H1-31', 'H3-H1-31']
pt2 out drop ids: ['H3-H2-20', 'H3-H2-22', 'H1-H2-23', 'H1-H2-25']
pt3 out drop ids: ['H2-H3-18', 'H2-H3-22', 'H2-H3-24', 'H2-H3-26', 'H1-H3-32', 'H2-H3-32']
对比结果可知,间隔越小,网络丢包率越高,队列排队程度越长。
交换机仿真
在三端口的拓扑中,三端口合并即为一个交换机,如下图所示
整合了端口的交换机代码如下,主要功能包括初始端口,连接端口对应的设备
class Switch(object):
def __init__(self, env, address, port_num, send_rate, queue_limit, valid_time=None, ctrler=None, debug=False):
self.address = address # 交换机的地址
self.port_num_list = [i for i in range(port_num)] # 端口编号列表
self.port_list = [None for _ in range(port_num)] # 端口列表
# 生成每个端口
for port_i in range(port_num):
trans_list = self.port_num_list.copy() # 对象数组采用copy
trans_list.remove(port_i) # 生成port_i 对应的 转发端口列表
self.port_list[port_i] = Port(
env, switch_addr = self.address, port_id = port_i, trans_list=trans_list, valid_time=valid_time, table=None, send_rate=send_rate, queue_limit=queue_limit, debug=debug)
# 连接交换机内部的端口
for port in self.port_list:
trans_list = self.port_list.copy()
trans_list.remove(port) # 生成port 对应的 转发端口
port.setTransPort(target_list=trans_list) # 连接转发端口
# 设置转发表
def setTables(self, tables_list):
for i in range(len(tables_list)):
self.port_list[i].setTable(tables_list[i])
# 设置连接对象
def setLink(self, link_list, port_list):
"""
link_list : 连接对象(Port or Host)
port_list : 目标对象的端口编号,连接主机时为空
"""
# print([host.address for host in link_list])
# print(self.port_list)
for i in range(len(link_list)):
if type(link_list[i]) is Host:
# 设置(out)连接对象
self.port_list[i].setOutObj(link_list[i].ps)
# 设置(in)连接对象
link_list[i].setLink(self.port_list[i])
else:
# 设置(out)连接对象, 交换机与交换机之间均为 in-->out
self.port_list[i].setOutObj(link_list[i].port_list[port_list[i]].in_port)
def __repr__(self):
return "address :{}".format(self.address)
转发测试代码如下
def constArrival():
return 0.8 # time interval
def getTable1():
return {"H1-H2": 1, "H1-H3": 2} # 交换机的端口编号 从0开始
def getTable2():
return {"H2-H1": 0, "H2-H3": 2}
def getTable3():
return {"H3-H1": 0, "H3-H2": 1}
if __name__=='__main__':
port_rate = 1200.0
env = simpy.Environment() # Create the SimPy environment
h1 = Host(env, address = "H1", dst_address=["H2", "H3"], interval=constArrival, rec_arrivals=True, debug=False)
h2 = Host(env, address = "H2", dst_address=["H1", "H3"], interval=constArrival, rec_arrivals=True, debug=False)
h3 = Host(env, address = "H3", dst_address=["H1", "H2"], interval=constArrival, rec_arrivals=True, debug=False)
swicth = Switch(env=env, address = 'S1', port_num=3, send_rate=port_rate, queue_limit=1001, debug=False)
link_list = [h1, h2, h3]
swicth.setLink(link_list=link_list, port_list=[])
swicth.setTables([getTable1(), getTable2(), getTable3()])
env.run(until=UNTIL_TIME)
print("received: {}, queuing: {}, out dropped {}, sent {}".format(
h1.ps.packets_rec + h2.ps.packets_rec + h3.ps.packets_rec,
len(swicth.port_list[0].out_port.queue)+ len(swicth.port_list[1].out_port.queue) + len(swicth.port_list[2].out_port.queue),
len(swicth.port_list[0].getOutDrop())+ len(swicth.port_list[1].getOutDrop()) + len(swicth.port_list[2].getOutDrop()),
h1.pg.packets_sent + h2.pg.packets_sent + h3.pg.packets_sent))
print("h1 received ids: {}".format(h1.ps.arrivals_id))
print("h2 received ids: {}".format(h2.ps.arrivals_id))
print("h3 received ids: {}".format(h3.ps.arrivals_id))
print("pt1 queue ids : {}".format([pkt.id for pkt in swicth.port_list[0].out_port.queue]))
print("pt2 queue ids: {}".format([pkt.id for pkt in swicth.port_list[1].out_port.queue]))
print("pt3 queue ids: {}".format([pkt.id for pkt in swicth.port_list[2].out_port.queue]))
print("pt1 out drop ids: {}".format([pkt.id for pkt in swicth.port_list[0].out_port.out_drop]))
print("pt2 out drop ids: {}".format([pkt.id for pkt in swicth.port_list[1].out_port.out_drop]))
print("pt3 out drop ids: {}".format([pkt.id for pkt in swicth.port_list[2].out_port.out_drop]))
结果如下:
received: 35, queuing: 34, out dropped 3, sent 72
h1 received ids: ['H2-H1-1', 'H3-H1-1', 'H2-H1-3', 'H3-H1-3', 'H2-H1-5', 'H3-H1-5', 'H2-H1-7', 'H3-H1-7', 'H2-H1-9', 'H3-H1-9', 'H2-H1-11']
h2 received ids: ['H1-H2-1', 'H3-H2-2', 'H1-H2-3', 'H3-H2-4', 'H1-H2-5', 'H3-H2-6', 'H1-H2-7', 'H3-H2-8', 'H1-H2-9', 'H3-H2-10', 'H1-H2-11', 'H3-H2-12']
h3 received ids: ['H1-H3-2', 'H2-H3-2', 'H1-H3-4', 'H2-H3-4', 'H1-H3-6', 'H2-H3-6', 'H1-H3-8', 'H2-H3-8', 'H1-H3-10', 'H2-H3-10', 'H1-H3-12', 'H2-H3-12']
pt1 queue ids : ['H3-H1-11', 'H2-H1-13', 'H3-H1-13', 'H2-H1-15', 'H3-H1-15', 'H2-H1-17', 'H3-H1-17', 'H2-H1-19', 'H3-H1-19', 'H2-H1-21', 'H3-H1-21']
pt2 queue ids: ['H1-H2-13', 'H3-H2-14', 'H1-H2-15', 'H3-H2-16', 'H1-H2-17', 'H3-H2-18', 'H1-H2-19', 'H3-H2-20', 'H3-H2-22', 'H1-H2-23', 'H3-H2-24']
pt3 queue ids: ['H1-H3-14', 'H2-H3-14', 'H1-H3-16', 'H2-H3-16', 'H1-H3-18', 'H2-H3-18', 'H1-H3-20', 'H2-H3-20', 'H1-H3-22', 'H2-H3-22', 'H1-H3-24', 'H2-H3-24']
pt1 out drop ids: ['H2-H1-23', 'H3-H1-23']
pt2 out drop ids: ['H1-H2-21']
pt3 out drop ids: []
比较三端口和交换机仿真可知,交换机主要是将端口的初始化和连接关系进行整合封装,提高效率。
然而,目前交换机的转发规则时固定的,如需要动态改变转发策略,则需要控制器的加入,并动态下发转发流表。
欢迎通过企鹅(488128665)进行交流!
以上仿真内容代码下载:基于Simpy/Python的通信网络仿真工具