前言
内网穿透作为程序员常用的调试手段之一,我们可以通过在个人电脑上运行花生壳或者 frp 等方式,让他人访问我们本地启动的服务,而且这种访问可以不受局域网的限制,当我们使用ngrok,frp等开源框架时,你是否有好奇过它神奇的作用?明明没有将服务部署到服务器,程序员们究竟是怎么通过这种特殊方式让所有人访问自己的主机的?本文将以frp开源框架为例,介绍内网穿透的原理。
公网 IP 与内网 IP
能否在公网中访问服务器的决定性因素:公网 IP
IP 地址的作用
众所周知, IP 地址是每一位使用互联网的网民都会拥有的标识, IP 地址在互联网中起到的作用是定位,通过 IP 地址我们可以精确的定位到所需资源所在的服务器,这是对于一般用户来讲的,而对于程序员而言,我们需要的则是让用户通过 IP 地址定位到我们部署的资源,既然每个互联网用户都拥有 IP 地址,为什么用户无法直接访问部署在个人PC上的服务呢?
事实上, IP 地址分为两种:公网 IP 和内网 IP
- 内网 IP : 内网 IP 是用户在使用局域网时,由局域网的网关所分配的 IP 地址,每一个内网 IP 实际上都可以映射到当前所在局域网网关的某一端口( IPV4 地址通过 NAT 与端口映射方式实现,具体原理下文详解),拥有内网 IP 可以被同一局域网下的其他设备所访问到;
- 公网 IP : 内网的设备想要访问非同一局域网下的资源则必须通过公网 IP ,公网 IP 是没有经过 NAT 转换的由互联网供应商(ISP)提供的最原始的 IP 地址,每一个公网 IP 都可以直接在互联网中被直接定位到。
一个最简单的例子(以前端开发为例) :
当我们使用 webpack-dev-server 来启动一个 node 项目时,我们除了通过localhost:[端口号]的方式以外,与我们的开发设备处于同一局域网下的设备可以通过内网 IP :[端口号]的方式对我们的项目进行访问,但当我们使用自己的流量或者连接其他非当前开发设备所在局域网的设备使用内网 IP :[端口号]的方式进行进行访问时,则无法访问。
原因:
内网 IP 地址仅在当前局域网下可以被定位并访问到,而当我们想要跨局域网访问时,我们的访问请求则需要先映射为公网 IP 然后访问到另一局域网的公网 IP ,最后由另一局域网的网关将其映射到相应的局域网设备,但我们访问的地址属于局域网中的内网 IP ,因此无法定位到其相应的公网 IP
综上所述,当我们想要让处于其他局域网下的设备访问到我们本地资源,必不可缺的就是公网 IP 。
公网 IP 的稀有程度
相较于内网 IP ,公网 IP 明显比内网 IP 更加有用,为什么不可以人手一个公网 IP 呢?
IPV4和 IPV6
尽管 IPV6 的概念在几年前已经被提出,但实际的普及程度并没有很高,现在大部分网络用户使用的依旧是 IPV4 的 IP 地址,这也是限制公网 IP 个数的最大原因。
- ** IPV4:** IPV4 由 32 位二进制数组成,一共有 2^32 个不同的 IPV4 地址
- ** IPV6**: IPV6 由 128 位二进制数组成,理论上共有 2^128 个不同的 IPV6 地址
由此可见, IPV4地址的个数并不足以满足当前全世界网络用户的人手一个 IP 地址的需求,那么当前的网络为什么可以让这么多用户同时在网络上冲浪呢?
NAT(网络地址转换)技术
网络地址转化技术的核心作用在于实现对公网 IP 地址的复用,即所有的内网主机共用同一个 IP 地址,NAT 的实现方式共有三种:
- 静态转换:将内网 IP 直接转换为公网 IP 地址,形成一一对应的方式
- 动态转换:将内网 IP 地址转换为公网 IP 地址,与静态转换不同的是动态转换会在 IP 池中选择空闲 IP 地址进行转换,即每次同一个内网 IP 对应的公网 IP 会发生改变
- 端口多路复用(PAT 技术):将内网 IP 与公网 IP 的某一端口进行映射,通过公网 IP 的某一端口访问公网
可以看出以上三种形式中端口多路复用(PAT)技术可以最大程度上缓解 IPV4 地址紧张的现状,也是最为广泛使用的实现方式,三种 NAT 实现方式共同点在于:对于内网用户来说自己对应的公网 IP 是不可知的,就好像我们可以知道自己的门牌号但无法知道自己所在的小区,因此无法准确告诉别人我们的具体地址。
什么是内网穿透
内网穿透(又称为端口映射),也就是将本地内网端口映射到公网(
又称为外网),内网其实就是局域网,你在家或者任何公共场合想要访问到你们公司的内部网络,这是就需要内网穿透了。
将原本只能在本机上访问的项目网站等,通过内网穿透使得你可以在其他人的网上进行访问。
让外网能访问你的内网;把自己的内网(主机)当成服务器,让外网能访问。
公网是不能直接访问内网的机器的,使其能的过程就是内网穿透。
举几个例子
例子1:
学了网络之后,我们知道了公网和私网。私网是不能在公网传输和通信的。我们一个学校,一个小区,都是在自己单独的私网里面。通过这个私网内部的路由器(NAPT方式)和外界通信。
那么问题来了,假如我要和其他学校的小伙伴通信,那怎么办呢?我和小伙伴都是在单独的私网里面,而私网的地址是不能通信的,那么我怎么才能和他通信呢?这就要说到内网穿透了!
如下图,有这么一个环境。小明和小王分别是清华和北大的学生。他们都各自处在自己学校的内网中,他们的ip地址可以是一样的,都是192.168.10.2。清华大学的小明想约北京大学的小王晚上吃饭。于是小明想给小王发一个消息,约她晚上一起吃饭。那么,小明该如何给小王发消息才能让小王收到消息呢?小明只知道小王的ip地址和自己的一样,都是192.168.10.2。很显然,如果小明给192.168.10.2这个ip地址发消息,小王是肯定不能收到消息的。
那么我们就会想,平时我们使用QQ、微信和其他地方、其他学校的小伙伴进行通信,消息是怎么发送的呢?
原因在于QQ和微信在公网有服务器。我们和小伙伴通信是先将消息发送给公网的服务器,公网服务器再将消息发送给位于私网内部的其他小伙伴。数据在我们和小伙伴之间并不是直接传送的。
那么问题又来了?
我们发消息给公网服务器这个可以理解,我们知道他们的公网ip,数据可以到达。但是,公网服务器是如何将我们发送给他的消息发送给位于其他私网内部的小伙伴的呢? 公网服务器并不知道位于私网内部的小伙伴的ip地址。
就算他知道了小伙伴私网边界的路由器的公网ip地址,发消息给路由器的公网ip,路由器收到消息也不会发送给小伙伴,因为在路由器里面并没有一条记录说收到qq或者微信服务器发来的消息然后转发给小王。
就算路由器知道从qq或者微信服务器发来的这条消息是转发给小王的,小王收到这条信息之后也不会接受。因为出于安全起见,除非是主机主动向对方发起了连接请求(这时会在该主机的数据结构中留下一条记录)。否则,当主机接收到数据包时,如果在其数据结构中查询不到对应的记录,那些不请自来的数据包将会被丢弃。
那么我们究竟是如何通过QQ或者微信和其他学校的小伙伴通信的呢?
我们首先看一个简单的情形,位于私网中的我们要访问百度,我们知道百度的域名:www.baidu.com 。于是,我们在浏览器中输入www.baidu.com ,然后我们就可以在百度上查阅我们想查的东西了。域名解析的过程我们不去分析,这个不是本文的重点。这里假设已经将域名进行解析了,解析成了对应的公网ip。当我们访问这个公网ip的时候,我们把流量交给路由器,路由器再根据ip地址请求百度的服务器。百度服务器回消息给路由器,路由器再把消息发送给我们。但是,内网中有这么多机器都是共用路由器的这一个地址,假如同时有好多人访问百度,那么路由器是如何知道百度回的消息是送给小王而不是小李的呢?因为在路由器体内有一个私网ip和端口的对照表,每个私网ip对应一个端口,所以根据端口就能知道消息是发送给内网中的哪台主机了。而我们是主动访问百度的,所以在我们的主机中会有一条记录,当我们接收到百度回的消息时,会接收这条数据。
假如小明以22222端口(随机,大于1024即可)访问百度服务器的80端口,则在我们本地可以看到这么一条连接:
192.168.10.10:22222 <——> 119.75.217.26:80
但是在路由器或者百度服务器上看到的连接则是这样的
100.100.10.10:10000 <——> 119.75.217.26
在路由器体内有这么一条Session记录
192.168.10.10:22222 <——> 10000
当我们访问百度这个事情做完之后的一定时间内,Session记录就会在路由器的体内消失,这个10000端口可以继续分配给其他用户
那么现在我们再来分析一下位于内网中的我们如何和同样是内网中的小伙伴通过QQ通信。
首先,当我们登陆上QQ后,我们就会和QQ的服务器建立一个长连接,基于这个长连接,我们可以给QQ服务器发送消息,QQ服务器也可以给我们发送消息(原理和上面百度的类似)。
我们有小伙伴的QQ号,于是我们给小伙伴QQ号发消息。这个消息通过我们私网边界的路由器发送给了QQ的服务器。QQ的服务器根据QQ号,会查找小伙伴是否在线。如果小伙伴此时也登录上了QQ号的话,那么QQ服务器就会把消息发送给小伙伴。如果小伙伴此时没有登录QQ,那么QQ服务器将不会给小伙伴发送这条消息,因为QQ服务器此时并没有和小伙伴建立长连接,所以他根本不知道小伙伴的位置。只有等小伙伴登录了QQ之后,QQ服务器才会将我们的消息发送给小伙伴。然后小伙伴回消息,后面的过程和之前一样
所以,要让位于两个私网内部主机通信的话,必须得有一个公网的服务器来做中间人,帮我们传递消息。我们私网内部是不能直接通信的!
可见,内网穿透的核心原理在于将外网 IP 地址与内网 IP 地址建立联系,市面上常用的如花生壳工具其核心原理就是依靠一台具有公网 IP 的服务器作为请求的中转站以此来达到从公网访问内网主机的目的。
例子2:
A在家上网,B也在家上网。
有一款局域网游戏,AB想要一起玩。由于是局域网游戏,那么就需要A或者B其中一位,穿透广域网,去到另外一位的家里的局域网,成为局域网的一员。我们称这种行为叫内网穿透。
具体实现方法,就是在双方,局域网访问外网的路由上,设置NAT,当访问路由虚拟的身份的时候,路由就会转发到外网,去到对方的路由,而对方的路由,也虚拟一个身份,进行信息的对接收发。
再简单一点就是,两位家里各开一扇门,建一条专用的路连接这两扇门,连接你家和他家。不过这扇门有妖术,会让你自动化妆为对方家人。
内网穿透的原理很简单的说就是:两台计算机A和B都处于不同的局域网中,A想要访问B, 就需要通过一台服务器做桥接的,桥接的方式有两种,一种是服务器相互转发流量 到A和B,另一种是告诉对方公网IP地址,自己充当一个介绍人的角色。
例子3:
首先要知道什么是内网外网IP,IP好比你家的大门,别人到你家玩就必须通过这扇门。如果你家是别墅,别人可以直接找到你家大门,这就相当于你在外网有公网IP,外面人可以通过公网IP直接找到你。而你家如果是在小区的高楼里,这就相当于你家在内网只有内网IP,外人无法通过内网IP直接找到你,要先找到小区,再找到几栋几层,而进入小区之后,通过几栋几层几号找到你,这个过程就是端口映射,小区就充当了路由器的角色,而在疫情期间快递员不能直接送快递到你家门口,只能送到小区门口集中收件点,你要去这个收件点和快递员面铺面收快递,这个快递收件点给你实现的功能就是内网穿透。
内网穿透的工具有很多:花生壳、蜻蜓映射、frp、nps、ngrok
内网穿透实现方案
- 端口映射(Port forwarding)
- 反向代理(Reverse proxy)
- VPN(Virtual Private Network,虚拟专用网络)
- NAT 穿透(NAT Traversal)