客户端的第一步是先读取配置,从这里来看有两种配置,分别是 proxyvisitorproxy 我理解,但是 visitor 是什么东西还不太清楚,那先看 proxy 吧,看下有哪些配置项:

好像比较简单,但是似乎和配置不一样,例如 addressport 这些信息都没体现出现,所以这里需要探究一下转化的逻辑:

这里没啥好看了,NewService 只是做了一个结构体的初始化,然后应该核心还是在 Run() 里面:

从这里来看的职责也是比较简单的,忽略掉不感兴趣的,剩下的可能也就是 svr.ctl.Run() 了,不知道这里面又做些什么事情,结合 Server 端的表现,我猜测应该也是做连接管理的,应该认证也是在这里面完成的:

和猜测的还是比较相似的,首先是登录,这里登录是不断尝试的,10秒的尝试周期,如果不外介入的话这里会一直尝试下去,这里有两个事情很重要:

  1. 一个重要的东西是在 login() 里面设置的,那就是 RunId,这个 RunId 在整个 frp 中有很重要的定位作用,Client 端和 Server 端都是同步的;
  2. control 的 connection 是在 login() 里面设置的,应该后续和 Server 端的连接都通过这个 conn 进行

在登录下面有个 ctl.worker(),然后再后面有: CheckAndStartProxy,所以猜测 worker 的职责应该是消息的接收发送处理,但是连接应该不会在这里面处理:

这里和 Server 端的功能是对应的,但是在代码一致性上和 Server 端不一致,风格不一致;但是无妨,且先这么看着,这个函数下面还有一个地方值得注意的就是当连接意外中断之后,会有一个重连的尝试,重连尝试成功之后,一切数据结构都会被重置,这里就包括发送和结构队列,所以可能出现掉包的情况,这个需要注意

在认证登录完成之后应该就是 Proxy 链路的建立,第一个问题就是 Proxies 的信息从哪里来:

这里给出了答案

  1. 构建一个 ProxyMessage 给 Server
  2. Proxy 状态设置为 ProxyStatusWaitStart
  3. 然后等待 messageHandler 来处理

消息的处理逻辑应该是这样的:

这里似乎没啥内容,但是可以知道的是这里提取了响应体中的两个参数:ProxyNameRemoteAddr,看下具体的处理逻辑:

这里奇怪的是重新构建了一个 Proxy 对象,我猜测应该是为重连而特地做的处理?代码都在这里明面上展示了,但是连接管理不再这里展现,应该在 Run 里面,在跟踪 Run 函数的时候发现这是一个 Interface,所以得看 NewProxy 出来的对象是啥,再具体看对应的实现:

这里的 TCP 居然不做很多事情,所以到这里似乎只是保存了一条记录,没有做其他的设置了。

这里总结一下,Client 和 Server 端的整体交互图应该是这样的: