背景

最近工作中有公网访问内网服务的需求,便了解了内网穿透相关的知识。发现原理和实现都不复杂,遂产生了设计一个内网穿透的想法。

名字想好了,就叫QuantumTunnel,量子隧道,名字来源于量子纠缠现象。

两个处于量子纠缠的粒子,无论处于多么远的距离,当其中一个粒子状态改变时,另外一个粒子也会做出相应的改变。

QuantumTunnel也取意于此,希望把公网发出来的请求,完整的同步到内网,就像在内网发出的请求,打破网络的限制。

什么是内网穿透

什么是内网穿透?摘自百度百科

内网穿透,也即 NAT 穿透,进行 NAT 穿透是为了使具有某一个特定源 IP 地址和源端口号的数据包不被 NAT 设备屏蔽而正确路由到内网主机。

通俗易懂一点就是一个公网内的机器与不能被公网访问的机器进行数据交换。

这个不能被公网访问的机器有可能在某个机房,也有可能在家。

典型的应用场景

  1. 公网访问内网中的某个系统服务:出于安全等因素考虑,机房一般是不能被公网访问的;要想访问机房中的某个服务,就需要用到内网穿透;
  2. 开发者电脑接收公网回调:在开发微信业务时,一般会涉及到服务回调,而开发者电脑无法被公网访问,这时候也需要内网穿透进行桥接。

设计思路

  1. 首先,摆在面前的问题是如何让公网与内网通信。内网的机器不能被公网访问但是一般都能够访问公网(没有访问公网能力的场景不在讨论范围内)。可以利用这一点,让内网机器主动与公网的服务器建立双向通信连接,这样公网服务器就具备了往内网服务器发送数据的能力;
  2. 其次,要思考怎么才能让用户请求到达这个双向通信通道。此时就需要一个用户侧的服务器,专门处理来自用户的请求,并将其转发到双向通信通道;
  3. 上面两个步骤完成后,用户请求就来到了内网,此时需要一个客户端代替用户进行真正的请求,拿到返回结果。

归纳总结一下。为了实现内网穿透能力,内网穿透服务中应该有的几个角色:

  1. 用户服务器:处理用户过来的请求,将用户请求转发给代理服务器;
  2. 代理服务器:接收用户服务器过来的请求数据并转发给往代理客户端;接收代理客户端的结果数据并返回给用户服务器;
  3. 代理客户端:接收代理服务器的请求数据并且进行真正的请求,拿到结果数据后返回给代理服务器。

架构图

按照上述思路,内网穿透的架构应该是这样的:

我们再次梳理一下参与到内网穿透服务的各个角色:

  1. 用户客户端:真实的请求发起方;
  2. 内网穿透-用户服务端:接收用户客户端发起的请求;并将请求转发给代理服务端;
  3. 内网穿透-代理服务端:与代理客户端保持一个连接通道用于传输数据;并且将请求通过该通道传输数据到proxy-client;
  4. 内网穿透-代理客户端:从通道中接收来自代理服务端的请求数据,并且发起真正的请求。拿到请求结果后再通过该通道写回到代理服务端;
  5. 目标服务器:目标服务器,即被代理的服务器。

时序图

顺着这个思路,我们来画一下时序图。

时序图分为两部分:

  1. 首先,代理服务器和代理客户端建立了一条长连接,用于数据的传输,这个很关键。

    因为代理服务器(公网)无法直接访问代理客户端,必须由代理客户端主动向代理服务器发起连接请求,从而建立一条可以双向通信的连接通道,利用双向通信的能力实现代理服务器主动向代理客户端发送请求的能力

  2. 然后是一次内网穿透请求的流程。

    要注意的是对raw request(response)的处理:raw request -> proxy request ->; raw request,经历了从原始请求到代理请求再到原始请求的封装和解析过程。

    为什么要有这个协议转换过程?要实现协议无侵入(如http、ws)的目标,只能在现有协议上扩展请求。如内网穿透http协议,可以把目标地址和目标端口放在header中进行扩展,内网穿透-用户服务器再把相关的参数给解析出来,从而知道目标地址和端口是什么。这样处理的好处也显而易见,在原来的框架、代码不变的情况下,增加几个参数就可以用上内网穿透服务。

大家可以了解一下这两个内网穿透的实现,natx毒刺,我在设计实现过程中参考了他们的一些实现。

具体实现

结合上面的架构图、时序图,要想实现应用层协议无侵入,需要一个能直接在传输层进行流量代理的工具。在网络传输领域大火的netty便进入了我们的视线,支持TCP、UDP流量转发,拥有丰富的应用层协议插件,更重要的是发送数据非常方便,只需要往Channel里面写入数据就行。

对于具体实现,本文暂不讨论,计划放在QuantumTunnel系列博客中的第二篇展开。

下面看看实现的效果。

效果

假设南京本地宝的服务器在内网,我们现在要访问它的新闻咨询频道。原始链接为:http://nj.bendibao.com/news/

把目标服务器替换成南京本地宝服务器,重新画一下架构图和时序图:

  1. 架构图

  1. 时序图

  1. 访问结果

从返回的结果可以看到,通过本地的8090端口,访问到了南京本地宝的服务器,说明整个链路成功走通。

这里解释一下访问结果中的几个参数

proxyHost:被代理的服务器地址;proxyPort:被代理的服务器端口

好了,本篇就聊到这里。后面会推出一个系列博客聊一下基于Netty的实现方案,以及业务隔离、服务高可用的一个探索等好玩的东西。

开源地址

QuantumTunnel:内网穿透服务设计的更多相关文章

  1. frp + nginx 配置多人共用的http 内网穿透服务

    来源:简书   https://www.jianshu.com/p/c9d7527d607b 一. 前言 frp 是一个用Go语言开发的,可用于内网穿透的高性能的反向代理应用,支持 tcp, udp ...

  2. 四、frp内网穿透服务端frps.ini各配置参数详解

    [必须]标识头[common]是不可或缺的部分 [必须]服务器IPbind_addr = 0.0.0.00.0.0.0为服务器全局所有IP可用,假如你的服务器有多个IP则可以这样做,或者填写为指定其中 ...

  3. 手写内网穿透服务端客户端(NAT穿透)原理及实现

    Hello,I'm Shendi. 这天心血来潮,决定做一个内网穿透的软件. 用过花生壳等软件的就知道内网穿透是个啥,干嘛用的了. 我们如果有服务器(比如tomcat),实际上我们在电脑上开启了服务器 ...

  4. natapp 内网穿透服务

    参考文章:https://www.jianshu.com/p/91a321e584b8 参考文章:https://www.jianshu.com/p/c4cb8666c96a 一.什么是内网穿透 通过 ...

  5. Natapp内网穿透服务工具

    在做微信开发的时候,调用微信接口成功之后,微信会回调我们事先配置好的一个接口.由于微信的服务是在外网的,所以这个回调接口也只能是外网,而且微信要求回调接口只能是通过ICP备案的域名,不能使用IP,所以 ...

  6. 免费内网穿透服务Localtunnel

    Localtunnel 将为您分配一个唯一的可公开访问的 url,它将所有请求代理到您本地运行的网络服务器. 快速开始 全局安装 Localtunnel(需要 NodeJS)以使其在任何地方都可以访问 ...

  7. 【python + NATAPP】实现内网穿透的简易数据传输

    1. 服务端 接收两张图像的地址,返回这两张图像的相似度 import os, shutil, requests import cv2 import numpy as np import imgs_s ...

  8. nodejs内网穿透

    说明 本地服务注册,基于子域名->端口映射.公网测试请开启二级或三级域名泛解析 无心跳保活.无多线程并发处理 服务器端 请求ID基于全局变量,不支持PM2多进程开服务端.(多开请修改uid函数, ...

  9. 五、frp内网穿透客户端frpc.ini各配置参数详解

    [必须]标识头[common]是不可或缺的部分 [必须]frps服务端IPserver_addr = 0.0.0.00.0.0.0为FRP服务端IP,客户端要填写为服务端已配置的对应的IP,或者是服务 ...

随机推荐

  1. 搞不定 NodeJS 内存泄漏?先从了解垃圾回收开始

    通常来说,内存管理有两种方式,一种是手动管理,一种是自动管理. 手动管理需要开发者自己管理内存,什么时候申请内存空间,什么时候释放都需要小心处理,否则容易形成内存泄漏和指针乱飞的局面.C 语言开发是典 ...

  2. 分析 ajax 请求并抓取 “今日头条的街拍图”

    今日头条抓取页面: 分析街拍页面的 ajax 请求: 通过在 XHR 中查看内容,获取 url 链接,params 参数信息,将两者进行拼接后取得完整 url 地址.data 中的 article_u ...

  3. P3309-[SDOI2014]向量集【线段树,凸壳】

    正题 题目链接:https://www.luogu.com.cn/problem/P3309 题目大意 \(n\)个操作 在序列末尾加入一个向量\((x,y)\) 询问加入的第\(l\sim r\)个 ...

  4. Jmeter压力测试学习7--压测带token的接口

    前言 工作中我们需要压测的接口大部分都是需要先登陆后,带着token的接口(或者带着cookies),我们可以先登陆获取token再关联到下个接口.比如我现在要压测一个修改用户的个人的密码 场景案例 ...

  5. 未能加载文件或程序集“System.Net.Http

    前言 简单说先事情的起因吧,之前的程序写了有一段时间了,最近要添加新的功能.顺手就把NuGet包全部更新到最新版.随之问题就出现了. 开始以为是.NET Framework 库的原因,之前是4.6.1 ...

  6. K8ssandra入门-详细记录在Linux上部署K8ssandra到Kubernetes

    1 什么是K8ssandra Cassandra是一款非常优秀的开源的分布式NoSQL数据库,被许多优秀的大公司采用,具有高可用.弹性扩展.性能好等特点. 正应Cassandra的优势,我们经常需要在 ...

  7. 关于java实体类时间类型的格式化调整问题

    关于java bean在后台\转化为json交给前台时间类型格式调整的方法: 首先要引入fastjson依赖. 在实体类上使用注解: @JsonFormat(pattern = "yyyy- ...

  8. 1.1 jvm核心类加载器--jdk源码剖析

    目录 前提: 运行环境 1. 类加载的过程 1.1 类加载器初始化的过程 1.2 类加载的过程 1.3 类的懒加载 2. jvm核心类加载器 3. 双亲委派机制 4. 自定义类加载器 5. tomca ...

  9. 多图详解万星 Restful 框架原理与实现

    rest框架概览 我们先通过 go-zero 自带的命令行工具 goctl 来生成一个 api service,其 main 函数如下: func main() { flag.Parse() var ...

  10. 题解 有标号DAG计数

    题目传送门 题目大意 给出\(n\),求出对于任意\(t\in[1,n]\),点数为\(t\)的弱联通\(\texttt{DAG}\)个数.答案对\(998244353\)取模. \(n\le 10^ ...