消息路由

如果给定节点想要与其他单个节点通信,泛洪不是一种非常有效的方法。 特别是当网络很大时,这会导致大量无用的数据传输。

另一种方法是为消息设置节点到节点的传输方式,直到它们到达目的地。 这样做的困难在于,它需要网络布局的知识。 为了向远方的鸟巢发送请求,有必要知道哪个邻近的鸟巢更靠近其目的地。 以错误的方向发送它不会有太大好处。

由于每个鸟巢只知道它的直接邻居,因此它没有计算路线所需的信息。 我们必须以某种方式,将这些连接的信息传播给所有鸟巢。 当放弃或建造新的鸟巢时,最好是允许它随时间改变的方式。

我们可以再次使用泛洪,但不检查给定的消息是否已经收到,而是检查对于给定鸟巢来说,邻居的新集合,是否匹配我们拥有的当前集合。

  1. requestType("connections", (nest, {name, neighbors},
  2. source) => {
  3. let connections = nest.state.connections;
  4. if (JSON.stringify(connections.get(name)) ==
  5. JSON.stringify(neighbors)) return;
  6. connections.set(name, neighbors);
  7. broadcastConnections(nest, name, source);
  8. });
  9. function broadcastConnections(nest, name, exceptFor = null) {
  10. for (let neighbor of nest.neighbors) {
  11. if (neighbor == exceptFor) continue;
  12. request(nest, neighbor, "connections", {
  13. name,
  14. neighbors: nest.state.connections.get(name)
  15. });
  16. }
  17. }
  18. everywhere(nest => {
  19. nest.state.connections = new Map;
  20. nest.state.connections.set(nest.name, nest.neighbors);
  21. broadcastConnections(nest, nest.name);
  22. });

该比较使用JSON.stringify,因为对象或数组上的==只有在两者完全相同时才返回true,这不是我们这里所需的。 比较 JSON 字符串是比较其内容的一种简单而有效的方式。

节点立即开始广播它们的连接,它们应该立即为每个鸟巢提供当前网络图的映射,除非有一些鸟巢完全无法到达。

你可以用图做的事情,就是找到里面的路径,就像我们在第 7 章中看到的那样。如果我们有一条通往消息目的地的路线,我们知道将它发送到哪个方向。

这个findRoute函数非常类似于第 7 章中的findRoute,它搜索到达网络中给定节点的路线。 但不是返回整个路线,而是返回下一步。 下一个鸟巢将使用它的有关网络的当前信息,来决定将消息发送到哪里。

  1. function findRoute(from, to, connections) {
  2. let work = [{at: from, via: null}];
  3. for (let i = 0; i < work.length; i++) {
  4. let {at, via} = work[i];
  5. for (let next of connections.get(at) || []) {
  6. if (next == to) return via;
  7. if (!work.some(w => w.at == next)) {
  8. work.push({at: next, via: via || next});
  9. }
  10. }
  11. }
  12. return null;
  13. }

现在我们可以建立一个可以发送长途信息的函数。 如果该消息被发送给直接邻居,它将照常发送。 如果不是,则将其封装在一个对象中,并使用"route"请求类型,将其发送到更接近目标的邻居,这将导致该邻居重复相同的行为。

  1. function routeRequest(nest, target, type, content) {
  2. if (nest.neighbors.includes(target)) {
  3. return request(nest, target, type, content);
  4. } else {
  5. let via = findRoute(nest.name, target,
  6. nest.state.connections);
  7. if (!via) throw new Error(`No route to ${target}`);
  8. return request(nest, via, "route",
  9. {target, type, content});
  10. }
  11. }
  12. requestType("route", (nest, {target, type, content}) => {
  13. return routeRequest(nest, target, type, content);
  14. });

我们现在可以将消息发送到教堂塔楼的鸟巢中,它的距离有四跳。

  1. routeRequest(bigOak, "Church Tower", "note",
  2. "Incoming jackdaws!");

我们已经在原始通信系统的基础上构建了几层功能,来使其便于使用。 这是一个(尽管是简化的)真实计算机网络工作原理的很好的模型。

计算机网络的一个显着特点是它们不可靠 - 建立在它们之上的抽象可以提供帮助,但是不能抽象出网络故障。所以网络编程通常关于预测和处理故障。