内网穿透工具frp核心架构原理分析

frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP、UDP、HTTP、HTTPS 等多种协议。可以将内网服务以安全、便捷的方式通过具有公网 IP 节点的中转暴露到公网。

本文是对frp v0.1.0 版本源码阅读后所做的分析,后续的版本大体上也是这个架构。frp v0.1.0 git分支d0a5400

frpc和frps之间的控制流

frpc和frps之间维持了一个长tcp连接,通过这个tcp连接进行控制指令的传递

心跳包 请求包
心跳包 控制包
frpc
frps

frpc请求包2种类型:

  • frpc启动时,发送请求启动proxy server监听端口包
  • 当收到frps命令时,请求建立工作tcp连接包,该工作tcp连接用于传输用户数据

frps控制包1种类型:

  • 给frpc发送指令,让frpc建立一条向frps指定端口的工作tcp,并在这个tcp上发送请求建立工作tcp连接包,以便frps能够识别出这个工作tcp,并将该工作tcp与用户tcp绑定

用户、frps、frpc、目标地址之间的数据流

工作步骤示意图

用户tcp 步骤1 步骤4
控制tcp 步骤2
工作tcp 步骤3
本地tcp 步骤3
用户
frps
frpc
目标地址
  1. 用户访问frps指定端口

  2. 通过控制tcp连接 frps向frpc发送建立一个新的工作tcp指令

  3. 收到frps指令后 frpc新建一条本地tcp和工作tcp,并将这两个tcp绑定。之后通过工作tcp发送该工作tcp所属的代理名至frps,一边frps识别。

  4. 收到工作tcp上的请求后,通过代理名找到对应的用户tcp,将工作tcp与用户请求tcp绑定。此时,用户和目标地址之间就建立起如下所示的tcp链路,就可以开始互相传输数据啦!

    用户tcp
    工作tcp
    本地tcp
    用户
    frps
    frpc
    目标地址

    实现细节

    frp绑定两个tcp连接的函数

    go语言

    // will block until connection close
    func Join(c1 *Conn, c2 *Conn) {
    	var wait sync.WaitGroup
    	pipe := func(to *Conn, from *Conn) {
    		defer to.Close()
    		defer from.Close()
    		defer wait.Done()
    
    		var err error
    		_, err = io.Copy(to.TcpConn, from.TcpConn)
    		if err != nil {
    			log.Warn("join conns error, %v", err)
    		}
    	}
    
    	wait.Add(2)
    	go pipe(c1, c2)
    	go pipe(c2, c1)
    	wait.Wait()
    	return
    }
    

你可能感兴趣的:(其它,架构,网络,tcp/ip,go,网络协议)