【socket】关于Unix域套接字(Unix Domain Socket)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了【socket】关于Unix域套接字(Unix Domain Socket)相关的知识,希望对你有一定的参考价值。

参考技术A 现实世界中两个人进行信息交流的整个过程被称作一次通信(Communication),通信的双方被称为端点(Endpoint)。
工具通讯环境的不同,端点之间可以选择不同的工具进行通信,距离近可以直接对话,距离远可以选择打电话、微信聊天。这些工具就被称为 Socket。

同理,在计算机中也有类似的概念:
在 Unix 中,一次通信由两个端点组成,例如 HTTP 服务端和 HTTP 客户端。
端点之间想要通信,必须借助某些工具,Unix 中端点之间使用 Socket 来进行通信。

Socket 原本是为网络通信而设计的,但后来在 Socket 的框架上发展出一种 IPC 机制,就是 UDS。
Unix Domain Socket(UDS,Unix 域套接字),它还有另一个名字叫 IPC(inter-process communication,进程间通信)。

使用 UDS 的好处显而易见:不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。这是因为,IPC 机制本质上是可靠的通讯,而网络协议是为不可靠的通讯设计的。

UDS 与网络 Socket 最明显的区别在于,网络 Socket 地址是 IP 地址加端口号,而 UDS 的地址是一个 Socket 类型的文件在文件系统中的路径,一般名字以 .sock 结尾。

这个 Socket 文件可以被系统进程引用,两个进程可以同时打开一个 UDS 进行通信,而且这种通信方式只会发生在系统内核里,不会在网络上进行传播。

mysql 在本地可以通过 socket 方式连接。
在本地登录时,如果 my.cnf 配置文件中的 [client] 部分没有指定 socket 文件路径,mysql 默认会去寻找 /tmp/mysql.sock(编译装)或 /var/lib/mysql/mysql.sock(rpm安装),所以如果 mysqld 服务启动的时候,生成的 socket 文件不是默认路径的话,登陆可能会报错(ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock')。

其实 [mysqld] 部分及 [client] 部分都配置具体路径可避免此问题,也可以在 tmp 路径下建立软连接,如:ln -s /pah/to/mysql.sock /tmp/mysql.sock 。

同样的,socket 文件目录权限要对 mysql 系统用户放开。

在docker官方文档docker-engine中,介绍了三种访问docker Remote API的方式,分别是unix套接字文件、tcp监听端口和fd文件描述符。

MySQL中的pid与socket是什么?
https://mp.weixin.qq.com/s/MehZ8GeRD9SBm8PxfGugRw

网络协议之:socket协议详解之Unix domain Socket
http://www.flydean.com/17-unix-domain-socket
https://mp.weixin.qq.com/s/4RwASqJu3Vc_suslKSbBkw

如何给UNIX域Socket套接字抓包?
https://mp.weixin.qq.com/s/SrV9_RouBJtZaUgG4WlamQ

Unix域套接字(Unix Domain Socket)介绍
https://mp.weixin.qq.com/s/psmAL216huaqytwrbcoIMw

关于/var/run/docker.sock文件的理解
https://cloud.tencent.com/developer/article/1454335

Daemon socket option
https://docs.docker.com/engine/reference/commandline/dockerd/#description

使用 Unix Domain Socket 与上游集群通信
https://mp.weixin.qq.com/s/5i_y1O7bSafAW7I51S7N8w

彻底搞懂文件描述符fd
https://www.itqiankun.com/article/file-fd

可以在Unix域套接字上使用SO_REUSEPORT吗?

Linux内核> = 3.9允许通过设置SO_REUSEPORT来共享内核负载平衡进程之间的套接字:http://lwn.net/Articles/542629/

如何将它用于AF_UNIX类型的插座?

看来,它只适用于TCP,而不适用于Unix域套接字。

这是一个Python测试程序:

import os
import socket

if not hasattr(socket, 'SO_REUSEPORT'):
   socket.SO_REUSEPORT = 15

if True:
   # using TCP sockets
   # works. test with: "echo data | nc localhost 8888"
   s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
   s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
   s.bind(('', 8888))
else:
   # using Unix domain sockets
   # does NOT work. test with: "echo data | nc -U /tmp/socket1"
   s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
   s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
   try:
      os.unlink("/tmp/socket1")
   except:
      pass
   s.bind("/tmp/socket1")

s.listen(1)
while True:
   conn, addr = s.accept()
   print('Connected to {}'.format(os.getpid()))
   data = conn.recv(1024)
   conn.send(data)
   conn.close()

启动2个实例,并通过多次运行以下测试:

  • 用于TCP的echo data | nc localhost 8888
  • echo data | nc -U /tmp/socket1用于Unix域套接字

使用TCP时,传入的客户端将与两台服务器保持平衡。使用Unix域套接字,传入的客户端都连接到最后启动的服务器。

答案

这个特定的内核补丁在这里记录:

http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=c617f398edd4db2b8567a28e899a88f8f574798d

从修补文件列表中可以看出,补丁仅影响了net/ipv4net/ipv6套接字。 Unix域套接字在net/unix中实现。所以,答案是:不,SO_REUSEPORT不适用于AF_UNIX类型的套接字。

另一答案

一个小补丁,在UNIX套接字SO_REUSEPORT上增加了对was posted的支持,但是被拒绝了。但是这个补丁并没有在多个套接字上实现负载均衡,如果套接字文件已经存在,它只会导致bind()不会失败。

这个用例被认为是

从用户的角度来看,这是一个非常奇怪的角落案例

因此,仍有可能通过SO_REUSEPORT实现UNIX套接字负载平衡的不同补丁将被接受。

以上是关于【socket】关于Unix域套接字(Unix Domain Socket)的主要内容,如果未能解决你的问题,请参考以下文章

C++ 使用 send_to 提升 unix 域套接字

如何给UNIX域Socket套接字抓包?

《Unix 网络编程》15:Unix 域协议

D-Bus 可以在不是 Unix 域套接字的流上使用吗?

Unix域套接字简介

PHP to Go 使用 Unix 域套接字