2019.12.5 更新:内网穿透还有更简单、更好的实现方法,具体看文末

前言

平时带一台轻薄的笔记本,打开远程家里/寝室里的台式,进行远程桌面,在有网速保证的前提下,既能获得笔记本的轻便型,也能获得台式电脑的强大性能,更能最大化笔记本的续航,更重要的是可以在同一台机器上进行办公、开发,无需资料同步。是很不错的选择。

我体验过的几款远程桌面软件里,

  • Teamviewer 是一个很优秀的选择;
  • 向日葵免费版有 300kbps 的限速,几乎等于不能用;
  • Anydesk 国内网速太慢,不可用;
  • 应急的话,还可以用 QQ 的远程协助。(用 QQ 的远程协助启动 Teamviewer 的远程协助)

除了以上选择以外,其实微软自带了远程桌面 mstsc。在局域网内你就可以通过远程 *.*.*.*:3389 (你的电脑 ip)体验到远程桌面,体验同样是相当不错的,几乎和 Teamviewer 是一样的(所以一般的同学就可以直接选 Teamviewer 了)。

但是,如果在外网体验的话,就不大好了。原因是,你在外网里找不到被远程端的 ip 地址(未经强调,本文所有 ip 指 ipv4,此处是因为 ipv6 在中国还没普及)。

NAT 及内网穿透的原理

ip 是一台机器在互联网上的唯一地址,可以通过你的机器 ip,从互联网的任何一端找到你的机器。

然而, ipv4 的数量是极其有限的,只有 256*256*256*256=4294967296 四十亿个(想想现在地球多少人口)。
而且还有很多 ip 是保留的,不能作为互联网里的 ip。
现在的 ip 地址已经快要用完了。

因此,聪明的运营商们想了一个办法,可以用一个设备,把他的一个 ip,下发为一万个 ip(当然,远不止这么多)。
这种方法叫 NAT(Network Address Translation,网络地址转换)。
好处是 ip 够用了,另外还具有防火墙的功能;坏处是,转换过的 ip 是不能在互联网上直接访问到的,而且还是动态变化的。你就不能直接靠输入 ip 来实现远程内网 ip。(所以互联网上能直接访问到的 ip 又叫公网 ip)

但是,你想想,肯定是会有方法的,要是你的电脑和一个有公网 ip 的电脑建立了联系,要想访问你的电脑,就可以通过找到这个 ip,让公网 ip 电脑转发一下数据到你的电脑上,你就可以内网以外的地方访问到内网设备了(实现的这个结果叫内网穿透)!
这便是转发/反向代理,内网穿透的一种实现。

2019.12.5 更新:另一种实现是 P2P,你的内网电脑和想要访问内网的电脑同时连接一台公网服务器,这台公网服务器就在你的两台电脑之间建立了一个 P2P 的联系,接下来,你的电脑就不需要再经过公网服务器的中转,直接访问内网电脑了。但这种方法只适用于 UDP 协议,不适用于 TCP 协议。ZeroTier 即是用这种方法实现的。

内网穿透需要一台有公网的电脑,最简单的办法,就是去租一个服务器。而实现转发数据、内网穿透的软件也有不少,如 frp、ngrok 等等。

(以下两段复制自 使用 Shadowsocks 搭建回国代理

租服务器

首先,去租一个阿里云或腾讯云的服务器。学生的话,都是一月10元即可(找不到可以在知乎搜一下相关回答)。

推荐使用 Debian 或 Ubuntu。

然后租了服务器会给 ip 地址(下面为方便叙述,设为 39.1.2.3)和密码。

ssh 远程登录

注意阿里云的服务器要开放防火墙的端口,不然连不上!!!!!!!!
作者就是被这个坑了一下午还没弄好。上面需要开放 22 端口,协议选 tcp

在本地 wsl(或自行百度 ssh 的方法)使用 ssh -p22 root@39.1.2.3 登录远程服务器。

下载并配置 frp

参考链接:https://www.hostloc.com/thread-463360-1-1.html

本地和远程下载 frp 的压缩包,然后解压。

注意下对应系统最新的包,而不是复制粘贴下面的命令(我遇到 i/o deadline reached 什么的奇奇怪怪的错误就是因为下的 0.9.0 版本的)。

服务器端的命令:

1
2
3
4
wget https://github.com/fatedier/frp/releases/download/v0.29.0/frp_0.29.0_linux_amd64.tar.gz
tar -zxvf frp_0.29.0_linux_amd64.tar.gz
cp -r frp_0.29.0_linux_amd64 /etc/frps
rm -f frpc frpc_full.ini frpc.ini

frp 的压缩包中同时包含了 server 服务器版本 frps 和 client 客户端版本 frpc。服务器可以删除 frpc 相关文件,客户端(被远程的电脑)可以删除 frps 相关文件。

然后配置服务器的 frps.ini 和客户端的 frpc.ini

服务端 frps.ini 一行即可,指明客户端连入的端口:

1
bind_port = 7000

客户端 frpc.ini 要复杂一点。

1
2
3
4
5
6
7
8
[common]
server_addr = 39.1.2.3 # 这里是 vps 的 ip
server_port = 7000
[rdp]
type = tcp
local_ip = 127.0.0.1
local_port = 3389 # 本地的 Remote Desktop 对应端口
remote_port = 5200 # 服务器接收 Remote Desktop 信息的端口,可以改

几个端口解释一下:

  1. 首先,服务器启动
  2. 客户端启动访问服务器的 7000 端口,建立内网穿透
  3. 远程的设备访问服务器的 5200 端口,服务器即把数据转发给客户端的 3389 端口,即可远程桌面

注意开放服务器防火墙 tcp 7000 和 5200 端口!!!! 我被这后面这个坑了一个小时。明明配置好了,就是连不上,原来是服务器没开端口。

另外还需要配置一下客户端的远程桌面方面的设置,这个比较常见,在 控制面板-系统-(侧边栏)远程设置-(单选框)允许远程连接到此计算机

启动 frp 及守护进程

服务器 /etc/frp/frps -c /etc/frp/frp.ini,客户端 frpc.exe -c frpc.ini

启动以后,要是有什么问题,建议检查端口开放和 frp 版本是否为最新,然后再百度。(对我各被坑了一个小时)

如果没什么问题,就可以测试远程桌面了。

注意,远程桌面连接的时候可能会提示密码错误(即使你密码输入正确了)。
这大概是个 bug,原因是你远程那边登录使用的不是密码(而是 PIN)。说不定是个为了安全的 feature 呢
解决方法是,远程端那边需要想办法注销账户,然后用密码登录,这边再远程就没有说明问题了。

要是可以正常使用,就可以准备守护进程了。

服务端后台运行

服务端使用 nohup /etc/frp/frps -c /etc/frp/frps.ini & &> /dev/null 使 frp 后台运行。

可能会看到 ignore input 之类的警告,不用管,Ctrl+C 退出前台即可,此时 ssserver 正在后台运行。

客户端开机运行

下面的方法二选一。

客户端需要开机后台启动 frpc,可以把 vbs 脚本放在 Startup 目录。

新建一个文本文档,加入下面两行脚本代码,并改名为 startup-frpc.vbs,复制到 C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp 下。

(假设 frpc.exefrpc.ini 都在 c:\frp\ 目录)

1
2
set ws=WScript.CreateObject("WScript.Shell")
ws.Run "c:\frp\frpc.exe -c c:\frp\frpc.ini",0

也可以用 任务计划,用 GUI 设定一下就行。具体百度吧,不难。

额外说一句,在设置任务计划时,需要注意的是,

  • 属性-常规-安全选项,中,选择不管用户是否登录都要运行,同时可以选择使用最高权限运行。(保存的时候需要输入账户的密码)
  • 触发器在系统启动时
  • 操作启动程序,命令为 wscript,参数为 "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp\startup-frpc.vbs"。(你也可以把这个 vbs 放在别的目录,然后把这个目录修改为对应的目录)

为了防止意外,可以在远程的电脑上安装额外的临时的远程桌面解决方案如 Teamviewer 或 QQ 以应急。

客户端守护进程

2019.12.5 更新:如果正确的配置启动命令以后,应该不会出现问题。所以也可以选择上面的配置。不过我还是选择的下面的配置。

但是有些时候还是会有蜜汁启动不了。即使我设了启动命令 + 任务计划,仍然必须要登录以后才会启动 frpc(而不是在开机后登陆前就启动了)。因此我并不推荐开机自启的方法,而是用这种进程守护的功能。

于是我打算搞一个进程守护,任务计划每一分钟启动一次,检测 frpc 是否正在运行,否则后台启动 frpc

为此我需要以下文件(都放在 D:\Documents\Tools\frp\):

  1. vbs 脚本,让命令行程序在后台运行的最简单的实现方法。内容如下:
1
2
set ws=createobject("wscript.shell")
ws.run"D:\Documents\Tools\frp\frpc-daemon.bat",0,ture
  1. frpc-daemon.bat 脚本,因为 bat 检测进程最为方便。vbs 也可以检测进程,但是按网上 Google 到的方法都只能检测会话名为 Console 的进程,对于任务计划启动的 Services 进程是检测不到的,再加上我不会 vbs,于是采用的是 vbs+bat 的方法。
    bat 内容如下:
1
2
set frppath=D:\Documents\Tools\frp
tasklist | find "frpc" || %frppath%\frpc.exe -c %frppath%\frpc.ini
  1. 任务计划。

常规部分,选择“不管用户是否登录都要运行”,不要勾选“不存储密码”。
触发器部分,选择 一次,时间默认,然后勾上 重复任务间隔 设为 1 分钟。
操作部分,启动程序 wscript,添加参数为 "D:\Documents\Tools\frp\frpc-daemon.vbs"(你的 frpc-daemon.vbs 路径,带引号)。

虽然这种方法会占用一点点 CPU(实际上占用的很少很少),但是真的非常推荐,配置好以后,要是哪天手滑关了 frpc,问题也不大,几分钟以后就会启动。并且配置好以后,也会开机自启,非常好用,推荐。

番外:为 RDP 使用 UDP 协议

远程桌面应该使用 TCP 还是 UDP 协议呢?

抛去网上的一堆 TCP/UDP 枯燥难懂的定义,我找到了这个问答

大概意思就是,UDP 只传输数据,不执行校验等命令。也就是说,较 TCP,

  • UDP 有更低的延迟(不执行命令)
  • UDP 牺牲了稳定性(不能防丢包等)

在 Windows 8 中,微软已经在 UDP 协议上启用了 RDP 协议。

至于是否使用 UDP,还是看自己(还有当地的网络情况)。

如果想要使用 frp 通过 UDP 协议传输,需要把客户端 frpc.ini 配置的部分再抄一遍,把 type 改为 UDP 即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
[common]
server_addr = 39.1.2.3 # 这里是 vps 的 ip
server_port = 7000
[rdp]
type = tcp
local_ip = 127.0.0.1
local_port = 3389 # 本地的 Remote Desktop 对应端口
remote_port = 5200 # 服务器接收 Remote Desktop 信息的端口,可以改
[rdp-udp]
type = udp
local_ip = 127.0.0.1
local_port = 3389
remote_port = 5200

如果想禁用 UDP,一个方案是把 frpc.ini 的 UDP 部分删掉;也可以通过(在两个设备)组策略关掉远程桌面使用 UDP 的
选项,在组策略的以下位置:

1
计算机配置/Windows 设置/管理模板/Windows 组件/远程桌面服务/远程桌面会话主机/连接/选择 RDP 传输协议

部分错误及解决方案

frps 和 frpc 无法连接

  • 如果客户端无法连接通过 frpc 连接到服务器,可能是服务器没有开放防火墙的对应端口;
  • 如果显示 i/o deadline reached 等奇奇怪怪的错误,检查一下 frps 和 frpc 是不是最新版。

frp 建立,但无法远程桌面

可能是服务器没有开放防火墙的对应端口。

mstsc 字体模糊

关闭根据网络调整质量,可改为256kbps-2M,然后开启平滑字体。

如果仍然无效,可能需要远程登录成功、退出以后重新登录。

2019.11.30 更新:如果网络不好,这个真的没法解决,只能选择将就着用,或者换 Teamviewer。

mstsc 和 Teamviewer 打开文件资源管理器时卡顿(更新于 2019.10.21)

必然发生,每次都会卡 5 秒左右。

在远程的电脑是 Win10 1903 时,两款软件都会出现这样的问题;升级到 1909 后,问题均消失。

大概 Teamviewer 也调用了 Windows mstsc 的 api 吧,然后 mstsc 出锅了。

后记

利用 frp,还可以进行一些骚操作:把自己的电脑当做云盘(比云服务提供的大)、配合 Shadowsocks 和学校寝室的电脑搭建校园网的代理。总之,几个东西的搭配,就能搞很多东西出来了。

内网穿透的其他方案

内网穿透还有其他实现方案,如果使用 P2P 的方案,这样所有流量就不用跑服务器转一圈了,而是两台电脑的流量直连,不会受限于两台电脑连接服务器的速度,也不会消耗服务器的流量。

引用知乎上 iittttt 用户的总结,目前用的较多的方案有:

项目名 开/闭源 P2P/非P2P 免费/收费
n2n 开源 P2P 免费 服务器较少,暂停开发
ngrok 分版本开源 非P2P 免费或收费 官方服务器被墙
反向ssh 开源 非P2P 免费
TeamViewer 闭源 P2P 免费或收费
Hamachi 闭源 P2P 免费或收费
花生壳 闭源 非P2P 免费或收费 免费限速

除此之外,推荐(100 台设备以下)免费的 ZeroTier,能够开机自启。使用带 GUI 的客户端来进行配置,配置过程参考这篇博客。不过缺点是,没有加入的设备,必须要安装客户端配置以后才能使用(frp只用配置服务器和内网设备,其他设备即可通过端口直接访问)。