V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
Contextualist
V2EX  ›  分享创造

又一个命令行文件传输,这次用边缘函数做 TCP 打洞

  •  
  •   Contextualist ·
    Contextualist · 2023-01-16 08:17:23 +08:00 · 3205 次点击
    这是一个创建于 712 天前的主题,其中的信息可能已经有所发展或是发生改变。

    简单来说,就是用 Deno deploy 这个跑 V8 的平台做建立点对点连接时打洞用的公共服务器。实现主要归功于 Deno deploy 的以下特性:

    • 作为 HTTP 服务器的 handler ,却可以拿到底层 TCP 连接的源 IP 和端口信息
    • BroadcastChannel API 让全球不同区域的实例可以实时通信

    具体可以参见我边缘函数的实现

    噱头归噱头,我也是真心想做一个比市面上已有的命令行文件传输更好用的工具。现有的工具设计时大部分是考虑传文件给他人的场景,但对于我来说,更多是在自己的不同服务器 /设备间传文件的场景。因此,大可不必每次传输都要求用户复制粘贴一段随机代号,身份验证信息可以提前存储到每台设备上。

    acp 使用体验上尽量贴近 cp ,发送端指定文件,接收端不需要指定任何信息。并且启动无分先后,两边都就绪后即开始协商建立连接传输。

    安装脚本详见 README(就下载个可执行文件然后初始化配置)

    最后忍不住说一句,边缘函数干这个实在是太适合了,轻量低成本低延迟,还颇有拆手机锂电池用来生火的风味

    项目在这: https://github.com/Contextualist/acp

    26 条回复    2023-01-29 18:34:43 +08:00
    v2wtf
        1
    v2wtf  
       2023-01-16 09:20:42 +08:00
    问一下,多人同时使用的情况下,是如何判断谁发给谁的?是需要事先登录同一个账号?
    Contextualist
        2
    Contextualist  
    OP
       2023-01-16 09:34:12 +08:00
    @v2wtf 对于每个人的第一台设备,acp 在初始化配置 (acp --setup) 时会生成一个随机 ID ,随后的设备初始化配置会导入这个 ID (acp --setup-with '{"id":"... )。使用时会配对相同 ID 发起的、时间上最接近的两个请求。本来这个设计是给每个人自己给自己传文件用的,如果多个人间使用导入相同的配置也行,不过不太适合群发或者临时分享给陌生人。
    AS4694lAS4808
        3
    AS4694lAS4808  
       2023-01-16 09:35:27 +08:00
    如果是自己的服务器间传文件,用已经信任的密钥+scp+zerotier 应该也挺方便吧?
    我是写了个 shell 脚本,接受的参数是(服务器名,源文件 /文件夹地址,远程目录)。。
    haoxuexiaoyao
        4
    haoxuexiaoyao  
       2023-01-16 09:37:26 +08:00
    安全么 经过服务器么
    haoxuexiaoyao
        5
    haoxuexiaoyao  
       2023-01-16 09:37:35 +08:00
    可以局域网部署么
    duke807
        6
    duke807  
       2023-01-16 10:48:13 +08:00 via Android
    “边缘函数” 是什么意思?
    Contextualist
        7
    Contextualist  
    OP
       2023-01-16 10:56:30 +08:00
    @AS4694lAS4808 诚然,scp 的地位无可替代,跟它相比,acp 主要是为了省去打服务器名和远程目录的功夫。我自己的使用场景是:我登上一台机器,在其中某个目录深处工作到一半,需要把一些文件拷到当前目录下,作为接收只需要运行个不带参数的 acp ,然后发送端运行 acp path/to/files 即可。

    @haoxuexiaoyao 服务只用来做 TCP 打洞的信息交换,真正的文件通过点对点直连传输。所以服务端接收发送的数据只有两边的 ID 和 IP 地址端口。部署到 Deno Deploy 同时也是为了利用他们全球分布的节点,服务端就一个 Deno TypeScript 文件,理论上来说你在任意 vm 上拿 Deno 也能跑。
    Contextualist
        8
    Contextualist  
    OP
       2023-01-16 11:46:18 +08:00
    @duke807 边缘函数类似云函数,用户托管的代码只需要包含一个处理 HTTP 请求的函数。相比一般的云函数,边缘函数的运行时是 V8 (Chrome 和 Node.js 的 Javascript 引擎) 而不是完整的容器,因而部署成本低并且冷启动非常快。
    Actrace
        9
    Actrace  
       2023-01-16 12:19:04 +08:00
    这个项目太棒了!
    lysS
        10
    lysS  
       2023-01-16 12:28:23 +08:00
    我只知道 UDP 打洞的原理,TCP 打洞是不是要 root 权限发送 raw ip 包?
    duke807
        11
    duke807  
       2023-01-16 12:34:27 +08:00 via Android
    @Contextualist
    云函数又是什么?
    不用容器不就是经典的直接运行的 nodejs 之类的程序,不用 docker 而已么
    novolunt
        12
    novolunt  
       2023-01-16 13:27:35 +08:00
    没配置吗? receiver 怎么接收指定的 sender ,没看到配置
    建议使用 zstd 压缩,性能比 gzip 好
    novolunt
        13
    novolunt  
       2023-01-16 13:42:24 +08:00
    测试了下,可以穿透内外网,很棒。
    AS4694lAS4808
        14
    AS4694lAS4808  
       2023-01-16 13:53:18 +08:00 via Android
    @duke807 公有云提供的服务,你把代码传上去,不用管运行环境,公有云提供软硬件运行你的代码
    Contextualist
        15
    Contextualist  
    OP
       2023-01-16 14:12:17 +08:00
    @lysS 其实原理跟 UDP 打洞差不多,大概都是两边先各自跟第三方建立连接,再重用端口尝试互相连接。随着路由和设备支持的逐渐完善,现在 TCP 打洞成功率还是挺高的。不需要 root ,TCP socket 可以设置 SO_REUSEADDR 和 SO_REUSEPORT ,使同一个端口像 UDP 那样能同时被多个 socket 绑定。

    @duke807 云函数基本就是你把代码交给平台,运维完全交给他们,平台根据请求数量伸缩实例数量,按照实际运行消耗的 CPU 时间和内存计费,没请求时不收费。边缘函数平台的 V8 比普通 Node.js 增加了权限隔离和横向伸缩等等,基本上像一个不带操作系统的容器了。

    @novolunt 谢谢建议!我去看看
    gogf
        16
    gogf  
       2023-01-16 17:22:57 +08:00
    反馈个问题:

    Win10: acp --setup 之后,acp 发送文件,报错:failed to communicate with the bridge: io: read/write on closed pipe

    Linux 下正常

    而且 Windows 下貌似换个文件夹重新 setup ,id 、psk 就变了
    Contextualist
        17
    Contextualist  
    OP
       2023-01-16 18:03:29 +08:00
    @gogf 谢谢反馈!我找了台 Windows 的机子,没法复现,这两个现象我暂时没头绪,不过我可以尝试解释一下。
    > 报错:failed to communicate with the bridge: io: read/write on closed pipe
    能连上配对的公共服务,但是收发信息时连接断开了,不过我在服务端也没看见报错日志。
    > 貌似换个文件夹重新 setup ,id 、psk 就变了
    这就很奇怪,acp --setup 会去找 %APPDATA%\acp\config.json 配置在不在,找不到才会重新生成。你的 %APPDATA% 环境变量有发生改变吗?你能在那个位置找到配置吗? acp --setup 有其他报错吗?
    gogf
        18
    gogf  
       2023-01-16 19:00:38 +08:00
    @Contextualist
    环境变量没有发生变化,acp --setup 也没有报错信息,我后面测试正常了,配置文件存在

    不过我可以确定两次生成的参数确实不一样,应该是第一次因为某些原因没写进去

    收发文件的问题还存在
    Contextualist
        19
    Contextualist  
    OP
       2023-01-16 19:38:18 +08:00
    @gogf 我盯着代码又仔细想了一下,很有可能在连接配对公共服务时就出错了,但是这个错误没有被及时检查,被后来的收发信息错误覆盖了。所以可以快速尝试的事是看看你在终端里能不能正常访问 https://acp.deno.dev/get ,类似用 curl 发个 get 请求。如果这个也是正常的,你又不嫌麻烦,我明天修了这个报错顺序的问题,你再看看有没有新的错误信息。总之,感谢你的反馈!
    gogf
        20
    gogf  
       2023-01-17 10:15:47 +08:00
    @Contextualist
    确实无法访问,浏览器与 curl 都无法访问,挂了代理就行,移动宽带

    curl 报错:curl: (35) schannel: failed to receive handshake, SSL/TLS connection failed

    终端在可以正常 get “https://acp.deno.dev/get“的前提下,acp 还是无法正常使用,同样的报错内容
    Contextualist
        21
    Contextualist  
    OP
       2023-01-17 11:32:05 +08:00
    @gogf
    我大概明白了,你是在用 HTTPS_PROXY 之类的环境变量吗?这个目前的实现会无视代理的环境变量。我修了这个问题后自己试了一下,发现用代理时建立不了 P2P 连接,你如果想试一下可以试试目前最新提交的 CI artifacts: https://github.com/Contextualist/acp/actions/runs/3935796582 这个页面最底下可以下载。我得花时间想想这个问题是不是能解决的。

    如果一定需要代理,我自己试了一下目前那种基于 fake IP 的真全局代理(比如 ClashX Pro 的增强模式)是可以用的。
    gogf
        22
    gogf  
       2023-01-17 14:01:09 +08:00
    @Contextualist
    我原本是没有设置代理的,我的意思是设置了 HTTPS_PROXY 才能访问 “ https://acp.deno.dev/get

    另外,https://github.com/Contextualist/acp/actions/runs/3935796582 这里好像并不能下载最新版,不知道没有权限还是什么,没用过 GitHub Actions...
    Contextualist
        23
    Contextualist  
    OP
       2023-01-17 14:32:22 +08:00
    @gogf 嗯,我需要研究一下怎么样正确支持 HTTPS_PROXY ,等到时候有结果了跟你说。

    看了一下,GitHub Actions 好像需要登录才能下载 artifacts ,任意账户都行。如果不行,你也可以用我下载下来的这个: https://t.wss.ink/f/aa11yspjrlv
    Contextualist
        24
    Contextualist  
    OP
       2023-01-18 20:24:32 +08:00
    @gogf 实在是抱歉!我折腾了半天,还是没能解决跟代理一起使用的兼容问题,下面是初步结论(但感觉解释不太清楚

    对于使用代理的一方,只有代理服务器连接 acp.deno.dev 时使用的 IP 端口是对外可见的,但是这个 IP 端口实际上并不能被其他连接重用。
    如果另一方没使用代理并且有公网,那或许可以连接上,但是实际测试发现,使用代理的一方不能设置端口重用(似乎是 Go 的 bug )。

    对此,剩下的选择是你自己部署服务端(参见 https://github.com/Contextualist/acp/blob/main/docs/advanced.md#host-the-rendezvous-service-yourself ),在 Deno Deploy 上或者自己的服务器上都行,然后绑定一个自己的域名。
    sbilly
        25
    sbilly  
       2023-01-29 16:35:12 +08:00
    @Contextualist Linux/Windows/macOS 都支持吗?
    Contextualist
        26
    Contextualist  
    OP
       2023-01-29 18:34:43 +08:00
    @sbilly 都支持的,并且有相应的持续集成测试
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2818 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 02:22 · PVG 10:22 · LAX 18:22 · JFK 21:22
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.