UCB CS168 lab2 Routing 小记
简单记录一下我做下来的一些小坑。
Stage 2
正确的端口
事实上,handle_data_packet(self, packet, in_port)
注释说明了是
:param in_port: the port from which the packet arrived.,但这个其实没有作用。我们关心的是我们当前这个
router 要把收到的 packet 向哪个端口转发,所以其实是:
1 | dst = packet.dst |
Stage 4
注意 latency 计算完整
我们在接收到邻居的广播后,注意他发送过来的 latency 只是他到目标 host 的 latency,而我们如果要通过它中转,还需要加上我们到它的 latency:
1 | latency = route_latency + self.ports.get_latency(port) |
Stage 5
字典删除问题
由于 self.table
是一个字典类型,而直接在遍历中删除字典元素会引发错误:
1 | RuntimeError: dictionary changed size during iteration |
比较推荐的做法是先收集一个应当删除的 list:
1 | expire_list = [] |
Stage 6&7
关于处理 Split Horizon 和 Poison Reverse
有一个更聪明的写法,我们在遇到 self.SPLIT_HORIZON 为 True
的时候选择直接 continue,这样就避免了大量繁琐的 if 语句。
1 | for port in ports_update: |
Stage 10A
题干提示了要使用一个 self.history
的数据结构来记录每个端口向每个目的地上一次转发的 packet
的信息。我一开始使用了一个元组 (port, dst),然后使用
self.key: {(port, dst): RoutePacket}
这样的键值对来存储。
但这样有几个问题:
写起来很繁琐
有冗余的信息,我们不需要保存完整的 packet,因为我们的 key 已经有 dst 了(RoutePacket 本身有 latency 和 dst 两个信息)
综上,我们采用一个更良好的写法,即一个二重字典。
1 | def __init__(self, ...): |
defaultdict
对于一个普通的 Python 字典, 如果你访问一个不存在的键,它会直接报错 KeyError。 但 defaultdict(dict) 自带默认值:
当尝试访问 self.history[port] 时,如果这个 port 还没出现过,它不会报错,而是自动创建一个空的字典 {} 填在那里。
配套的,使用 dict.get(key, None)
可以安全的取出对应的值。