1. 概述

本教程,我们将介绍 Linux 中的 socat 命令。Socat 是一个灵活、多用途的中继工具。支持多种数据源: Unix socket, UDP, TCP, 和标准输入。

2. socat 使用场景

假设我们有两台服务器 A 和 B,主机 A 上运行着一个MySQL数据库,在内网外网无法访问,而主机 B 网络是开放的,并且B可以访问A。

如果有用户想查看主机 A 上的数据库日志,而我们又不希望用户直接进入主机 A,那么我们可以允许用户通过主机 B 访问日志。 Socat 可以将主机 A 上的数据库日志与主机 B 上的文本阅读器连接起来,这样用户就能在不接触主机 A 的情况下读取日志。

此外,Socat 还能实现双向通信。用户可以通过主机 B 向主机 A 上的数据库服务器发送查询请求,并接收到服务器的查询结果。Socat 让这一切变得安全又简单。

3. 安装 socat

大多数 Linux 发行版都提供了 socat 的安装包,所以安装非常简单。

3.1. Prerequisites

For the examples in this tutorial, we’ll use nc and Docker, which we can install from their user guides.

3.2. Debian 安装 socat

对于Debian、Ubuntu等,使用 apt-get 命令安装:

$ apt-get install -y socat

3.3. RHEL Linux 安装 socat

基于红帽的操作系统,例如Fedora,使用yum安装:

$ yum install -y socat

4. 双向流

We can use socat to connect two data sources and stream data between them.

4.1. 连接两个流

下面我们使用 nc 进行 双向数据流传输 测试。我们需要打开两个控制台窗口:

第一个窗口,执行 nc -l 命令

$ nc -l localhost 1234 

-l 表示 nc 处于监听模式,监听localhost上 1234 端口的连接。

第二个窗口运行 socat, 使用 TCP 协议将 STDIO 连接到本地主机的 1234 端口:

$ socat STDIO TCP4:localhost:1234

上面的命令中,第一个参数 STDIO 表示标准输入。第二个参数表示的是一个TCP连接地址,端口是1234。值得一提的是,因为它是双向的,参数的顺序无关紧要。

通过这行命令,socat 将标准输入的流连接到我们使用nc创建的TCP流上去了。

4.2. 测试流

Now, our standard input on the second terminal is connected to the nc server. We can type something on it:

Good morning!

Then, we go back to the first terminal. We can see that the text we typed on the second terminal was printed on the first terminal:

Good morning!

Then, we can type something below “Good morning!” on the first terminal:

Life is beautiful.

Then, we go back to the second terminal. We can see that “Life is beautiful.” was printed there as well:

Life is beautiful.

4.3. 转发流

Instead of handling the data itself, socat can forward the data that will be received by another socat application. This time, we need three console terminals.

On the first terminal, we run nc as usual:

$ nc -l localhost 1234

On the second terminal, we run socat. But this time, we don’t connect the standard input with the nc application on the first terminal. Instead, we listen on another port:

$ socat TCP4-LISTEN:4321 TCP4:localhost:1234

We used a different string syntax, TCP4-LISTEN:4321. It means we listen to the data that comes from the second argument, TCP4:localhost:1234, and write it to port 4321. When we used the TCP4-LISTEN address format, we only added the port. This is different from the TCP4 address format where we have to fill the server address.

On the third terminal, we’ll run socat, and this time, we connect the standard input to the socat application on the second terminal:

$ socat STDIO TCP4:localhost:4321 

Now, when we type something on the first terminal, we can see the text appear on the third terminal. The three terminals are networked via the second terminal acting as a relay.

4.4. 使用 socat 连接 docker

To make our example more practical, we’ll connect the webserver in a separate terminal with a web client. First, we need to pull the Nginx Docker image:

$ docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
1fe172e4850f: Pull complete 
35c195f487df: Pull complete 
213b9b16f495: Pull complete 
a8172d9e19b9: Pull complete 
f5eee2cb2150: Pull complete 
93e404ba8667: Pull complete 
Digest: sha256:859ab6768a6f26a79bc42b231664111317d095a4f04e4b6fe79ce37b3d199097
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest

We make sure port 80 is not occupied. Then, we run the nginx Docker image:

$ docker run -p 80:80 nginx

The run argument is to run the Docker image. The -p option maps port 80 on the container to port 80 on the host. If we didn’t do this, we wouldn’t be able to access the webserver from the host.

Then, on the second terminal, we run socat to connect to the Nginx server:

$ socat TCP-LISTEN:1234,reuseaddr,fork TCP:localhost:80

We’ve learned the first argument previously. TCP-LISTEN is to listen to data movement in port 1234. But here, there are other parts in the argument syntax: reuseaddr and fork. They mean that socat will fork a child process to processes connecting to this port.

On the third terminal, let’s run socat to connect the standard input to port 1234:

$ socat STDIO TCP4:localhost:1234

Finally, in the fourth terminal, let’s run the same command again:

$ socat STDIO TCP4:localhost:1234

Notice that we’re connecting two socat applications to the same port. This works because we use reuseaddr and fork in the TCP-LISTEN argument. If we didn’t use them, the same command on the fourth terminal would fail:

$ 2022/04/14 02:37:17 socat[4017376] E connect(5, AF=2 127.0.0.1:1234, 16): Connection refused

We can type anything to test the connection. We can do this on the third terminal or on the fourth terminal. Let’s test the web connection. We can use GET request:

GET /

We’ll get the output in the same terminal:

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
</html>

On the first terminal, we can see that socat passed the data just fine:

172.17.0.1 - - [28/Apr/2022:16:51:34 +0000] "GET /" 200 615 "-" "-" "-"

5. Unidirectional Stream

We’ve learned that socat is a bidirectional stream protocol by default. But we can use its unidirectional mode.

5.1. The -u Flag

We can create a file using socat. Let’s run the command below, type something, then press Enter, and finally exit:

$ socat -u STDIO FILE:test.txt,create
Hello socat!

We can check the content of the test.txt file:

$ cat test.txt
Hello socat!

The -u flag means that the stream of data runs from the first argument, STDIO, to the second argument, file text.txt. The second argument uses the FILE address format. As we can guess, this format accepts the name of the file and the mode of opening the file. In our example, the mode is create. There is no data streaming from the second stream to the first in this mode.

The order or arguments matters. If we switched the arguments, the result of the command would be different:

$ socat -u FILE:test.txt,create STDIO
Hello socat!

This time, the command displayed the content of the file. If we tried with a different and new file, the output would be empty:

$ socat -u FILE:baeldung.txt,create STDIO

When the first argument is the file, and STDIO is the second argument, socat reads the content of the file and displays it in the stdout. But when it’s in reverse, socat writes the input from stdin into the file.

5.2. The -U Flag

There’s another flag that does the reverse operation of the -u flag. The -U flag is the same as the -u flag but the order of the arguments is reversed.

Let’s run the previous command but with the -U flag, type something, then press Enter, and finally exit:

$ socat -U FILE:test.txt,create STDIO
Hello socat! This time we use the -U flag.

Then we can check the content of the file:

$ cat test.txt
Hello socat! This time we use the -U flag.

So basically, socat -u arg1 arg2 is the same as socat -U arg2 arg1.

6. 总结

In this article, we’ve introduced the command socat in the bidirectional mode. We started a series of examples by running nc and connecting to it using the TCP protocol and the standard input. Then, we verified that they could stream data between each other.

Additionally, we also connected the Docker container and the standard input using socat. Between them, we used the standard input and the TCP protocol.

Finally, we ran socat in unidirectional mode with the -u flag and the -U flag to read and write a file.