今天我们会来讲解一个特殊的socket,这个socket不需要使用传统的IP地址和端口,而是使用文件系统来进行程序之间的数据交互,并且这样的socket只能使用在unix系统上。这样的socket就是今天我们要讲解的Unix domain Socket。
什么是Unix domain Socket
什么是Unix domain Socket呢? 我们从名字就可以看出来,这个Socket是和unix domain有关系的,也就是说这个socket需要用到unix下面的一些特殊功能。
我们考虑下常用的windows系统和unix系统,他们最大的区别在哪里呢?
其实最大的区别就是unix操作系统中一切都可以看做是文件,包括程序运行的一些信息。
那么我们是不是可以直接借助于这些程序运行时产生的文件来进行不同程序之间数据的交互呢?答案是肯定的。这就是我们今天要讨论的Unix domain Socket。
Unix domain Socket可以简称为UDS,不同程序间的数据可以在操作系统层,借助于文件系统来进行数据交换。
对于程序本身来说,只需要读取和写入共享的socket文件即可,也就是说不同的程序之间通过socket文件来进行数据交互。
和基于IP和端口的Socket一样,Unix domain Socket也可以分为Stream Socket和Datagram Socket。
我们最多看到Unix domain socket的地方可能就是docker了,作为一种容器技术,docker需要和实体机进行快速的数据传输和信息交换,一般情况下UDS的文件是以.socket结尾的,我们可以在/var/run目录下面使用下面的命令来查找:
find . -name "*.sock"
如果你有docker在运行的话,可以得到下面的结果:
./docker.sock
./docker/libnetwork/6d66a24bfbbfa231a668da4f1ed543844a0514e4db3a1f7d8001a04a817b91fb.sock
./docker/libcontainerd/docker-containerd.sock
使用socat来创建Unix Domain Sockets
之前提到了socat这个万能的工具,不仅可以创建tcp的监听服务器,还能创建udp的监听服务器,当然对于UDS来说也不在话下。我们来看下使用socat来创建UDS服务器所需要用到的参数:
unix-listen:<filename> groups=FD,SOCKET,NAMED,LISTEN,CHILD,RETRY,UNIX
unix-recvfrom:<filename> groups=FD,SOCKET,NAMED,CHILD,RETRY,UNIX
这里我们要使用到unix-listen和unix-recvfrom这两个参数,unix-listen表示的是创建stream-based UDS服务,而unix-recvfrom表示的是创建datagram-based UDS。
可以看到两个参数后面都需要传入一个文件名,表示UDS socket的地址。
我们可以这样使用:
socat unix-listen:/tmp/stream.sock,fork /dev/null&
socat unix-recvfrom:/tmp/datagram.sock,fork /dev/null&
这里我们使用/tmp/datagram.sock来表示这个socket信息。
其中fork参数表示程序在接收到程序包之后继续运行,如果不用fork,那么程序会自动退出。
socat后面本来要接一个bi-address,这里我们使用/dev/null,表示丢弃掉所有的income信息。
运行后我们可能得到下面的结果:
[1] 27442
[2] 27450
表示程序已经成功执行了,返回的是程序的pid。
使用ss命令来查看Unix domain Socket
在使用ss命令之前,我们先来看下使用socat生成的两个文件:
srwxrwxr-x 1 flydean flydean 0 Mar 2 21:58 stream.sock
srwxrwxr-x 1 flydean flydean 0 Mar 2 21:59 datagram.sock
可以看到这两个文件的权限,rwx大家都懂,分别是read,write和执行权限。那么最前面的s是什么呢?
最前面的一位表示的是文件类型,s表示的就是socket文件。
扩展一下,这个位置还可以有其他几种选项:p、d、l、s、c、b和-:
其中p表示命名管道文件,d表示目录文件,l表示符号连接文件,-表示普通文件,s表示socket文件,c表示字符设备文件,b表示块设备文件。
接下来我们使用ss命令来查看一下之前建立的UDS服务。
这里需要使用到下面几个参数:
-n, --numeric don't resolve service names
-l, --listening display listening sockets
-x, --unix display only Unix domain sockets
这里我们需要使用到上面3个选项,x表示的是显示UDS,因为是监听,所以使用-l参数,最后我们希望看到具体的数字,而不是被解析成了服务名,所以这里使用-n参数。
我们可以尝试执行一下下面的命令:
ss -xln
输出会很多,我们可以grep我们需要的socket如下所示:
ss -xln | grep tmp
u_str LISTEN 0 5 /tmp/stream.sock 11881005 * 0
u_dgr UNCONN 0 0 /tmp/datagram.sock 11882190 * 0
u_str表示的是UDS stream socket,而u_dg表示的是UDS datagram socket。
我们可以使用stat命令来查看socket文件的具体信息:
stat /tmp/stream.sock /tmp/datagram.sock
File: ‘/tmp/stream.sock’
Size: 0 Blocks: 0 IO Block: 4096 socket
Device: fd02h/64770d Inode: 134386049 Links: 1
Access: (0775/srwxrwxr-x) Uid: ( 1002/ flydean) Gid: ( 1002/ flydean)
Access: 2022-03-01 22:33:21.533000000 +0800
Modify: 2022-03-01 22:33:21.533000000 +0800
Change: 2022-03-01 22:33:21.533000000 +0800
Birth: -
File: ‘/tmp/datagram.sock’
Size: 0 Blocks: 0 IO Block: 4096 socket
Device: fd02h/64770d Inode: 134386050 Links: 1
Access: (0775/srwxrwxr-x) Uid: ( 1002/ flydean) Gid: ( 1002/ flydean)
Access: 2022-03-01 22:33:22.306000000 +0800
Modify: 2022-03-01 22:33:22.306000000 +0800
Change: 2022-03-01 22:33:22.306000000 +0800
Birth: -
使用nc连接到Unix domain Socket服务
nc是一个非常强大的工具,除了可以进行TCP,UDP连接之外,还可以进行UDS的连接,我们需要使用到下面的参数:
-U, --unixsock Use Unix domain sockets only
-u, --udp Use UDP instead of default TCP
-z Zero-I/O mode, report connection status only
-U表示连接的是一个unixsocket。-u表示是一个UDP连接。
默认情况下nc使用的是TCP连接,所以不需要额外的参数。
另外我们直接建立连接,并不发送任何数据,所以这里使用-z参数。
先连接Stream UDS看看:
nc -U -z /tmp/stream.sock
如果没有输出任何异常数据,说明连接成功了。
然后再连接Datagram UDS看看:
nc -uU -z /tmp/datagram.sock
同样的,如果没有任何异常数据,说明Socket连接成功了。
我们详细介绍了Unix Domain Socket的含义,并且使用了unix中的一些工具实现了UDS的建立,检测和连接。基本上描述了UDS的使用情况。
作者:程序那些事
链接:https://juejin.cn/post/7075509542687080456
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
通过的linux命令socat和ncat可以去操作socket。
其中-U指定了该文件是Unix域socket文件类型,ncat实现了类似于cat命令的访问unix socket。
# 映射tcp的8080流量到unix socket
ncat -vlk 8080 -c 'ncat -U /tmp/tbsocket1'
# 通过curl发起http请求访问
curl http://localhost:8080
也可以使用功能更强大的socat来实现。
# 映射8080/tcp 到unix socket
socat -d -d TCP-LISTEN:8080,fork UNIX:/tmp/tbsocket1
nc(netcat)是一个短小精悍、功能实用、简单可靠的网络工具,主要有如下作用: (1)端口侦听,nc 可以作为 server 以 TCP 或 UDP 方式侦听指定端口; (2)端口扫描,nc 可以作为 client 发起 TCP 或 UDP 请求; (3)机器之间传输文件; (4)机器之间网络测速。
nc 实际上是 ncat 的软链接。ncat 是为 Nmap(Network Mapper) 项目编写的,是 Nmap 套件中的一员,它旨在成为可靠的后端工具,可立即为其他应用程序和用户提供网络连接。ncat 不仅可以使用 IPv4 和 IPv6,还可以为用户提供几乎无限的潜在用途。
nc [-46DdhklnrStUuvzC] [-i interval] [-p source_port] [-s source_ip_address] [-T ToS] [-w timeout] [-X proxy_protocol] [-x proxy_address[:port]] [hostname] [port[s]]
nc选项说明
-4/6
强制只使用 IPv4/IPv6 地址
-D
在套接字上启用调试
-d
不从 stdin 读取
-h
打印出帮助信息
-k
强制 nc 在当前连接完成后继续侦听另一个连接。注意如果不使用 -l 选项,则使用此选项是错误的
-l
指定 nc 应该侦听传入的连接,而不是启动到远程主机的连接。将此选项与 -p、-s 或 -z 选项结合使用是错误的。此外,使用 -w 选项指定的超时将被忽略
-n
不要在任何指定的地址、主机名或端口上执行任何 DNS 或服务查找
-r
随机选择源端口和目标端口,而不是按照系统分配的顺序或范围内的顺序选择它们
-S
启用 RFC 2385 TCP MD5 签名选项
-t
使 nc 发送 RFC 854 DON'T 和 WON'T 响应 RFC 854 的 DO 和 WILL 请求。这使得使用 nc 编写 telnet 会话脚本成为可能
-U
指定使用 Unix 域套接字
-u
使用 UDP 代替默认选项 TCP
-v
显示命令执行过程
-z
表示 zero,只扫描侦听守护进程,而不向它们发送任何数据。此选项与 -l 选项结合使用是错误的
-C
发送 CRLF 作为换行符
-i interval
指定发送和接收的文本之间的延迟时间间隔。还可指定连接到多个端口之间的延迟时间
-p source_port
指定 nc 应使用的源端口,但须受特权限制和可用性限制。将此选项与 -l 选项结合使用是错误的
-s source_ip_address
设置本地主机送出数据包的 IP 地址。注意将此选项与 -l 选项结合使用是错误的
-T ToS
指定连接的 IP 服务类型(TOS)。有效值是标记 ''lowdelay'', ''throughput'', ''reliability'',或以 0x 开头的 8 位十六进制值
-w timeout
如果连接和 stdin 空闲超过指定秒数,则连接将被关闭。-w 标志对 -l 选项没有影响。缺省不超时
-X proxy_protocol
请求 nc 在与代理服务器对话时使用指定的协议。支持的协议是 “4”(SOCKsv.4)、“5”(SOCKV.5) 和 “connect”(HTTPS proxy)。如果未指定协议,则使用 SOCKS v.5
-x proxy_address[:port]
使用指定代理服务器地址和端口连接到主机。如果未指定端口,则使用代理协议的已知端口(SOCKS为1080,HTTPS为3128)
nc 的控制参数不少,常用的几个参数如下所列:
-l
指定 nc 将处于侦听模式。指定该参数,则意味着 nc 被当作 server,侦听并接受连接,而非向其它地址发起连接
-p PORT
指定 nc 使用的源端口
-s
指定发送数据的源 IP 地址,适用于多网卡机器
-u
指定 nc 使用 UDP 协议,默认为 TCP
-v
输出交互或出错信息,新手调试时尤为有用
-w
超时秒数,后面跟数字
-z
表示 zero,扫描时不发送任何数据
常用示例
监听本地端口。
假设在当前命令行终端 A 进行监听。
nc -vl 8888
Ncat: Version 6.40 ( http://nmap.org/ncat )
Ncat: Listening on :::8888
Ncat: Listening on 0.0.0.0:8888
开启另外一个命令行终端 B,同样使用 nc 发起连接。
nc -v 127.0.0.1 8888
另一个终端 A 将会收到请求并打印连接信息:
Ncat: Connection from 127.0.0.1.
Ncat: Connection from 127.0.0.1:37229.
如果在终端 B 输入内容,那么终端 A 将收到终端 B 发送的内容并打印到标准输出。
利用 nc 之间的连接进行文件传输。
使用 nc 传输文件还是比较方便的,因为不用 scp 和 rsync 那种输入密码的操作了。把 A 机器上的一个文本文件发送到 B 机器上 需注意操作次序,receiver 先侦听端口,sender 向 receiver 所在机器的该端口发送数据。
receiver:
nc -l 8888 > received.txt
sender:
nc 127.0.0.1 8888 < file.txt
利用 nc 传输目录。
发送目录与发送文件类似,只不过是利用 tar 先将目录打包成一个文件,然后发送。接收方再利用 tar 进行解压缩还原为目录。
receiver:
nc -l 8888 | tar -xzvf -
sender:
tar -czvf - DIR_NAME | nc 127.0.0.1 8888
注意,tar -f 选项后跟横杠 - 表示从标准输入读取或将结果输出到标准输出。
测试网速。
测试网速其实利用了传输文件的原理,就是把来自一台机器的 /dev/zero 发送给另一台机器的 /dev/null 就是把一台机器的无限个 0,传输给另一个机器的空设备上,然后新开一个窗口使用 dstat 命令监测网速 在这之前需要保证机器先安装 dstat 工具。
第 1 步,在 A 机器先启动接收数据的命令,监听自己的 8888 端口,把来自这个端口的数据都输出给空设备(这样不写磁盘,测试网速更准确)。
nc -l 8888 > /dev/null
第 2 步,在 B 机器发送数据,把无限个 0 发送给 A 机器的 8888 端口。
nc 10.0.1.161 8888 < /dev/zero
在 A 机器新的窗口上使用 dstat 命令查看当前网速。dstat 命令比较直观,它可以查看当前 CPU,磁盘,网络,内存页和系统的一些当前状态指标。我们只需要关注 recv 和 send 两列,分别表示接收和发送的数据,另外注意数字后面的单位 B,KB,MB。