Linux Socket Unix Domain介绍

白文彬
2023-12-01

以下内容引述至《Linux/Unix系统编程手册》

Unix domain socket允许同一系统上不同进程之间相互通信的一种方式

Unix domain socket地址

在Unix domain中,socket地址以路径名来表示,domain特定的socket地址结构的定义如下所示:

struct socketaddr_un {
	sa_family_t sum_family;
	char sun_path[108];
}

sockaddr_un 结构中字段 sun_前缀是根据socket unix而来。sun_path字段长度是108字节或104字节,现代使用94字节。可移植应用程序在编码时应该采用最低值,并且在向这个字段写入数据时使用snprintf()或strncpy()以避免缓冲区溢出。

当用来绑定Unix domain socket时,bind会在文件系统中创建一个条目。文件的所有权将根据常规的文件创建规则来确定。这个文件会被标志为一个socket

有关绑定一个Unix domain socket方面还需要注意一下几点:

  • 无法将一个socket绑定到一个既有路径名上;
  • 通常会将一个socket绑定到绝对路径名上,这样这个socket就会位于文件系统中的一个固定地址处。当然,也可以使用一个相对路径名,但这个做法并不常见,因为它要求想要connect()这个socket的应用程序知道执行bind()的应用程序的当前工作目录;
  • 一个socket只能绑定到一个路径名上,相应地,一个路径名只能被一个socket绑定;
  • 无法使用open()打开一个socket
  • 当不再需要一个socket时可以使用unlink()

现实世界中的应用程序应该将Unix domain socket bind()到一个采取了恰当的安全保护措施的目录中的绝对路径名上。

UNIX domain中的流socket

服务器程序执行任务:

  • 创建一个socket
  • 删除所有与路径名一致的既有文件,## 这样就能够将socket绑定到这个路径上;
  • 为服务器socket创建一个地址结构,将socket绑定到该地址上,将这个socket标记为监听socket
  • 执行一个无线循环来处理进入的客户端请求,每次请求执行如下任务:
    – 接收一个连接,为该连接获取一个新socket cfd
    – 从已连接的socket中读取所有数据并将这些数据写入标准输出中
    – 关闭已连接的socket cfd

客户端程序执行任务:

  • 创建一个socket
  • 为服务器socket构建一个地址结构并连接到位于该地址处的socket上
  • 执行一个循环将其标准输入复制到socket连接上。当遇到标准输入中的文件结尾时客户端就终止,其结果是客户端socket将会被关闭并且服务器在从连接的另一端的socket中读取数据时会看到文件结束

Unix domain中的数据报socket

Unix domain socket传输的数据报的最大大小并没有规定。在Linux上可以发送一个相当大的数据报,其限制是通过SO_SNDBUF socket选项和各个/proc文件来控制的。

服务器程序首先创建一个socket并将其绑定到一个众所周知的地址上,然后进入一个无限循环,在循环中使用recvfrom()接收来自客户端的数据报,将接收到的文件转换成大小格式并使用通过recvfrom()获取的地址将转换过的文本返回给客户端

客户端创建一个socket并将这个socket绑定到一个地址上,这样服务器就能够发送响应了。客户端地址的唯一性是通过路径名包含客户端的进程ID来保证的,然后客户端循环,将所有命令行参数作为一个个独立的消息发送给服务器。在发送完每条消息之后,客户端读取服务器的相应并将内容显示在标准输出上。

Unix domain socket 权限

socket文件的所有权和权限决定了哪些进程能够与这个socket进行通信

  • 要连接一个Unix domain流socket需要在该socket文件上拥有写权限;
  • 要通过一个Unix domain流数据报socket发送一个数据报需要在该socket文件上拥有写权限

互连socket对

有时候让单个进程创建一对socket并将它们连接起来是比较有用的。这可以通过使用两个socket()调用和一个bind()调用以及对listen()、connect()、accept()(用于流socket)的调用或对connect()(用于数据报socket)的调用来完成。

#include <sys/socket.h>
int socketpair(int domain, int type, int protocol, int sockfd[2]);

socketpair系统调用只能用在Unix domain中,即domain参数必须被指定为AF_UNIX
socket的type可以被指定为SOCK_DGRAM或SOCK_STREAM
protocol参数必须为0, sockfd数据返回了引用这两个相互连接的socket的文件描述符
将type指定为SOCK_STREAM相对于创建一个双向管道(也被称为流管道),每个socket都可以用来读取和写入,并且这两个socket之间每个方向上的数据信道是分开的。
一般来讲,socket对的使用方式与管道的使用方式类似,调用完socketpair()后,进程会使用fork()创建一个子进程。子进程会继承父进程的文件描述符的副本,包括引用socket对的描述符。因此父进程和子进程就可以使用这一对socket来进行IPC了

Linux抽象socket名空间

所谓的抽象路径名空间是Linux特有的一项特性,它允许将一个Unix domain socket绑定到一个名字上但不会在文件系统中创建该名字,优势是:

  • 无需担心与文件系统中的既有名字产生冲突;
  • 没有必要在使用完socket之后删除socket路径名;
  • 无需为socket创建一个文件系统路径名

要创建一个抽象绑定就需要将sum_path字段的第一个字节指为NULL字节(\0)
这样就能够将抽象socket名字与传统UNIX domain socket路径名区分开来。

 类似资料: