使用 NAT 穿透访问 NAT 后面的 HTTP Server 还是用更加简单的方式?

这篇文章整整拖了 5 个月,生成的 timestamp 原来是 2 月 7 的,现在都 7 月了。。。

其实本来是一个很简单的 C/S 模型,机器人 HTTP Server 是应对机器人上本地局域网中的一个客户端(Web,桌面,安卓/iOS)。客户端连上机器的局域网,然后通过局域网的 IP 地址,访问机器人上的 HTTP Server,控制机器人。

但是后面有一个需求是,当机器人插上一个 3/4 G 网卡的时候,要复用机器人上的 HTTP Server 远程控制和访问机器人。那问题来了,该如何访问一个 NAT 后面的 HTTP Server 呢?

原来调研过几个方案。

  1. 用 SSH 反响隧道,把本地的 HTTP Server port 映射到公网上的某台服务器上。但是弊端很多,一个机器人 Server Port 对应一个公网 Server Port,机器人上会有很多不同后端工程师写的 HTTP Server Port,而且有几百台机器。映射关系数量是 HTTP Server Port * 机器人数量。机器人和服务数量起来的花,管理起来非常操蛋。

  2. NAT 打洞直接 P2P 通信。其实这个里面的内容很多,牵涉到 4 种 NAT 类型,UDP/TCP 打洞方式,还有对应的两个协议 STUN/TURN。在 Peer-to-Peer Communication Across Network Address Translators 这篇论文里面有详细的介绍。在这里不细讲,但是这种方法对应当前的模型仍然不可用。因为对于 Symmetric NAT,无法保证 Client 的每次 Request 都统一相同的 Port。

  3. 使用 Socket.io 作为一个长链接转发。当前就是用的这种方法,不过调用的方法有点丑陋。实际我们用了 JSON RPC 的方法调用。首先在 Socket.io Server 上定义转发 JSON 数据的事件转发到机器人,然后在机器人上面也定义事件接受转发,然后用这些 HTTP Meta JSON RPC 访问本地服务器的 HTTP Server,最后再通过一个定义好的 JSON RPC 返回到 Client。这个过程维护了两条 Socket.io 的长连接,定义了很多转发事件,基本上就是一个基于 Socket.io 的 JSON RPC。

不过最近看了一个做法 http://lifeofzjs.com/blog/2014/11/17/visit-server-behind-nat/。这个模型比 JSON RPC 更加简单和舒心。直接用一台服务器的 HTTP Server 进行转发 Socket.io 的 JSON RPC 到机器人,机器人返回的 Response 直接作为这台服务器的 HTTP Server Response 返回给 Client。这样只维护了一条 Socket.io 长连接,而且节约了 JSON RPC 的定义过程,而且通过公网的路由,/robots/:id 就可以对应访问不同机器人。简直舒心。