< 返回版块

2019-03-21 12:20    责任编辑:Mike

本文转载自:https://www.driftluo.com/admin/article/view?id=f1b2cbde-28d8-4ca7-b100-25ddfd7ed26e

P2P 网络系列(二)

我们必须得承认,哪怕经历了几十年的构建,底层网络依然是不安全的存在,工程师在硬件不安全的基础上构建一个相对安全的网络环境,而相对安全的概念实际上是非常微妙的。我们暂且不关心硬件上的意外丢包、断电、链路损坏等场景,将硬件层当成是安全的,在应用层、网络层依然有各种意外情况发生,例如:

  • 无法确定数据包是否到达、甚至连它是否一定能到达都是未知的;
  • 数据包到达后,远端节点无法响应(服务器繁忙)、响应在网络中丢失(断网、交换机配置错误)、响应被延时处理(缓存在队列中,未及时发送)等情况;
  • TCP 虽然能保证双方都在线的前提下,数据包能到达,但是,它的带宽是动态调整的,如果网络繁忙拥塞,延时时间是无法确定的;

在工程上,依然只能用尽力而为的态度去做,假定底层网络不会出现上诉问题,那么它的正常打开、通信、延时、关闭流程需要一个标准的协议去定义。

P2P 框架与 P2P 协议

记得在几个月之前,我在公司线下技术分享会中有过一个关于这个主题的分享,但那只是对于 Bitcoin 的网络协议的介绍,以及对未来我们自己 P2P 网络的畅想。更深入的地方因为本身没做过实现,就略过了。p2p 真正要实现的其实是无中心化、可自发现的一种网络,其他的根本不重要。

这系列讲的主要是 P2P 框架,对于具体协议的实现,只涉及多路复用协议 —— yamux 的实现问题,更上层的应用协议,就不在这个系列内了。

那么,为什么我们要实现首先实现一个框架,然后再在其之上构建用户层协议呢?

对多版本多协议共存的支持

在应用层,想要保证网络协议的向后兼容,或者说,想要更轻松地切换用户层协议,就必须先对底层的通用协议进行统一抽象,然后在统一抽象之上,就可以定义用户层协议的版本、协议号、协议编码规则等等内容,同时,我们也可以在统一抽象之上,做单连接多协议并存的实现。

那么,不做抽象和做抽象对支持多协议并行,协议多版本并行的区别在什么地方呢?

不做抽象,如果要支持多协议并行,协议多版本并行,伪代码如下:

if proto_name = "a" {
  if proto_version = "0.0.1" {
      do_a_something()
    } else if proto_version = ... {
      do_a_something()
    }
  ...
} else if proto_name = "b" {
  if proto_version = "0.0.1" {
      do_b_something()
    } else if proto_version = ... {
        do_b_something()
    }
  ...
 }
 ...
}

几乎所有判断,都必须在同一个地方去验证,然后根据分支去执行,如果版本号多或者并行协议多,那分支代码就会越来越膨胀,同时,当想要删除某个协议或者版本支持的时候,删除分支代码可能并没有想象中那么简单,可能并不是删分支这么简单,同样,要增加一个协议也是异常麻烦,这是因为多个协议耦合在一起了,随着时间推移,越来越少人能够完全理清每个分支的行为。

那么如果做框架抽象支持,代码会是怎么样的呢?

trait ProtocolMeta {
    fn name() -> string {
        ...
    }
    fn version() -> Vec<String> {
        ...
    }
}

trait ProtocalHandle {
    fn connected() {
       ...
    }
    fn received() {
       ...
    }
    ...
}

我会这么做,对协议应该有的元数据定义一个 trait,然后每个协议定义一个 handle trait 用来定义每个协议自己的特有行为,每个协议只需要根据自己版本去做自己的事就行了,在连接建立过程,就将会知道双方同一个协议支持的版本,之后的通信将根据协商的版本号进行。如果要删除某个版本的支持,只需要在对应的 version 分支中删除就可以了,如果想删除或者新增一个协议支持,只需要再次实现上诉两个 trait 然后插入框架的配置中就可以了。协议之间的耦合可以相对更轻松地拆除。

隐藏底层细节实现

除了上面对多协议多版本的支持之外,还有一个功能就是隐藏一些业务层不关心的操作,包括多协议的路由问题、加密通信问题、连接管理问题。

业务端只需要知道,某个连接被打开了,这个连接中某个协议被打开了,这些信息就足够了,更多的细节问题,不需要放在业务层考虑,分层构建代码,让每一层专注于自己的实现,高内聚而低耦合。

比如,底层真实的连接可以做到与业务层无关,可以是 TCP、UDP 等任意协议。

小结

这一篇还是没有进入正题,而是在阐述,我们要将 P2P 网络分层构建,虽然我们的真实网络场景是有各种不可预测的问题,但我们依然需要在其上构建一个相对安全的网络服务,将琐碎的事情通过层层封装进行过滤,留给上层更加干净的接口,让每一层专注于本层业务的实现。