今天有一个这样的需求:

  • 我们提供 HTTP 服务给另一个团队,在联合调试的时候,客户端只能从服务器发起请求;
  • 我们想把服务部署在本地的笔记本上,用于调试,但是,服务器和笔记本之间有 NAT 和防火墙,在服务器上的客户端只能访问服务器,不能访问到我们的笔记本;
  • 但是我们的笔记本也可以文档服务器;

这就是一个典型的内网穿透的需求了。我今天发现,socat 就可以做这样的事情。

如下图所示:server 端的 5.5.5.5 的 IP 可以被访问,但是笔记本上的 IP 不可以。

socat 内网穿透原理

思路是:

  • 我们打开两个 socat 进程,一个在本地笔记本上,另一个在 server 端;
  • Server 端的 socat 做一件事情:Listen 8075 端口,一旦有连接建立,就开始 listen 5678 端口,然后将所有 8075 端口收到的内容全部复制到 5678 ,将 5678 的内容复制到 8075
  • Client 端的 socat 做一件事情:不断去尝试连接 5.5.5.5:5678 这个地址,一旦能够建立连接,就跟 192.168.0.9:8000 (或者 127.0.0.1 建立连接),然后将 5.5.5.5:5678 的内容复制到 192.168.0.9:8000 ,将 192.168.0.9:8000 的内容复制到 5.5.5.5:5678;

客户端运行的命令是:

1

socat -d -d -d -v tcp:5.5.5.5:5678,forever,intervall=1,fork,reuseaddr tcp:192.168.0.9:8000

服务端运行的命令是:

1

socat -d -d -d tcp-l:8075,reuseaddr,bind=0.0.0.0,fork tcp-l:5678,bind=0.0.0.0,reuseaddr,retry=10

这样,运行在本地 192.168.0.9:8000 的服务就通过 5.5.5.5:8075 暴露出来了,当用户访问 5.5.5.5:8075 的时候,就如同访问 192.168.0.9:8000 一样:

  1. 用户和 5.5.5.5:8075 建立 TCP 连接,将请求发送给 5.5.5.5:8075,此时,收到 HTTP 请求的实际上是 Server 端 Socat 进程;
  2. Server 端的 Socat 开始 listen 5.5.5.5:5678;
  3. Client 端的 Socat 不断尝试连接 5.5.5.5:5678(按照上文中的参数,是每 1s 尝试一次), 这时候,因为第 (2) 步,Client 的 Socat 同 5.5.5.5:5678 的连接建立成功,因此 Client 端 Socat 也和 192.168.0.9:8000 建立了连接;
  4. Server 端的 Socat 将 HTTP 请求发送给了 Client 端 Socat;
  5. Client 端 Socat 将 HTTP 请求发送给了 192.168.0.9:8000;
  6. 当笔记本上的 HTTP Server 发送了 HTTP Response,Client 端 Socat 将 HTTP Response 发送给了 5.5.5.5:5678,收到这个响应的是 Server 端的 Socat;
  7. Server 端的 Socat 将 HTTP 响应发送给客户端;
  8. 客户端收到 HTTP Response。

参考: