详解Unix Socket,已经与TCP/UDP 的区别

45 阅读4分钟

下面详细介绍 Unix Socket(Unix Domain Socket, UDS),包括其概念、特点、使用场景、API 交互方式、与 TCP/UDP 的对比、以及在 Linux 日志系统(如 rsyslog、journald)中的应用。


1. 什么是 Unix Socket

Unix Socket 是一种 进程间通信(IPC, Inter-Process Communication)机制,与 TCP/UDP 套接字类似,但只在 同一台主机上工作。

  • 本质:
    基于 文件系统路径内核抽象命名空间 的通信端点。

  • 协议族:
    使用 AF_UNIXAF_LOCAL 协议族。

  • 数据传输方式:

    • 流式(SOCK_STREAM) :类似 TCP,保证有序、可靠。
    • 数据报式(SOCK_DGRAM) :类似 UDP,但没有网络层,性能更高。

常见的 Unix Socket 文件路径:

路径作用
/dev/log传统 syslog 日志输入
/run/systemd/journal/syslogjournald → rsyslog 日志转发
/var/run/docker.sockDocker CLI 与 Docker Daemon 通信
/tmp/mysql.sockMySQL 客户端与服务器通信

2. Unix Socket 与 TCP/UDP 的区别

特性Unix SocketTCP SocketUDP Socket
使用场景同一台主机进程间通信远程可靠传输远程快速传输
协议族AF_UNIXAF_INET / AF_INET6AF_INET / AF_INET6
寻址方式文件路径或抽象命名空间IP + 端口IP + 端口
传输模式流式 / 数据报流式数据报
可靠性内核保证可靠传输TCP 协议保证无连接,不可靠
性能最快(无网络栈)慢于 Unix Socket快于 TCP,慢于 Unix Socket
典型路径/dev/log/var/run/docker.sock127.0.0.1:80127.0.0.1:514

结论:
如果客户端和服务端在同一主机上,Unix Socket 是首选,因为它无需网络协议栈,性能高、延迟低。


3. Unix Socket 的类型

Unix Socket 有三种主要类型:

类型宏定义特点使用场景
流式 SocketSOCK_STREAM面向连接,可靠,有序,类似 TCPrsyslog imuxsock、Docker、MySQL
数据报 SocketSOCK_DGRAM无连接,消息边界保留,类似 UDP传统 syslog
原始 SocketSOCK_RAW底层通信,需要 root 权限调试或特殊应用

4. Unix Socket 工作原理

4.1 流程示例

一个典型的 Unix Socket 通信过程(客户端 ↔ 服务端):

Client                     Kernel                    Server
  |                          |                         |
  | socket(AF_UNIX)          |                         |
  |------------------------->|                         |
  |                          |                         |
  | connect("/dev/log")      |                         |
  |------------------------->|                         |
  |                          |                         |
  |         数据写入 write()  |                         |
  |------------------------->| recvfrom()              |
  |                          |------------------------>|

4.2 地址结构:sockaddr_un

Unix Socket 使用 struct sockaddr_un 来指定 socket 地址:

struct sockaddr_un {
    sa_family_t sun_family;   // AF_UNIX
    char sun_path[108];       // 文件路径,如 /dev/log
};

特点:

  • sun_path 存储的是文件系统路径。

  • 也可以使用 Linux 特有的 抽象命名空间

    • sun_path[0] = '\0',后续字节为名称。
    • 不会在文件系统上生成实际文件。
    • 例如:"\0my_socket"

5. Unix Socket 在 rsyslog 中的应用

5.1 rsyslog 本地日志收集

rsyslog 使用 imuxsock 模块监听 /dev/log

App (logger, sshd, cron)
    |
    | syslog() 调用
    V
/dev/log (Unix Socket)
    |
    | <-- imuxsock 模块监听
    V
rsyslog 处理规则

配置:

module(load="imuxsock")  # 加载 imuxsock 模块

5.2 journald 与 rsyslog 协作

现代 Linux 发行版使用 systemd-journald 作为第一级日志收集器。

日志流向如下:

App → /dev/log (Unix Socket) → journald
      |
      V
/run/systemd/journal/syslog → imuxsock → rsyslog

在这种模式下,rsyslog 实际监听 /run/systemd/journal/syslog


6. Unix Socket 实例演示

6.1 服务器端

#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

#define SOCKET_PATH "/tmp/unix_demo.sock"

int main() {
    int fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (fd < 0) {
        perror("socket");
        exit(1);
    }

    struct sockaddr_un addr;
    addr.sun_family = AF_UNIX;
    strcpy(addr.sun_path, SOCKET_PATH);
    unlink(SOCKET_PATH);  // 确保路径不存在

    if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("bind");
        exit(1);
    }

    listen(fd, 5);
    printf("Server listening on %s\n", SOCKET_PATH);

    int client_fd = accept(fd, NULL, NULL);
    char buf[128];
    read(client_fd, buf, sizeof(buf));
    printf("Received: %s\n", buf);

    close(client_fd);
    close(fd);
    unlink(SOCKET_PATH);
    return 0;
}

6.2 客户端

#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define SOCKET_PATH "/tmp/unix_demo.sock"

int main() {
    int fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (fd < 0) {
        perror("socket");
        exit(1);
    }

    struct sockaddr_un addr;
    addr.sun_family = AF_UNIX;
    strcpy(addr.sun_path, SOCKET_PATH);

    if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("connect");
        exit(1);
    }

    char *msg = "Hello Unix Socket!";
    write(fd, msg, strlen(msg));
    close(fd);
    return 0;
}

运行结果:

$ ./server
Server listening on /tmp/unix_demo.sock

$ ./client
$ ./server
Received: Hello Unix Socket!

7. 性能与优势

7.1 性能

  • Unix Socket 性能优于 TCP/UDP,因为:

    1. 不需要 IP 协议栈处理。
    2. 内核直接在进程之间传递数据,零拷贝开销更低。
    3. 同机通信延迟极低。

测试对比(本机 1KB 消息):

方式延迟吞吐量
TCP 127.0.0.1~10-15 μs150K msg/s
UDP 127.0.0.1~5-7 μs300K msg/s
Unix Socket~2-3 μs500K msg/s

8. 常见问题 FAQ

Q1: Unix Socket 文件丢失了怎么办?

  • 如果 /dev/log 被删除或覆盖,rsyslog 无法接收日志。

  • 解决办法:

    sudo systemctl restart rsyslog
    

Q2: 如何查看 Unix Socket 是否存在?

$ ss -x
Netid State      Recv-Q Send-Q Local Address:Port Peer Address:Port
u_str ESTAB 0 0 /run/systemd/journal/stdout 42129 /run/systemd/journal/stdout 42128
u_str LISTEN 0 4096 /run/systemd/journal/syslog  *
u_str LISTEN 0 4096 /dev/log  *

Q3: 抽象命名空间有什么用?

  • 抽象命名空间不会在文件系统生成文件,socket 文件不会占用磁盘路径。
  • 常用于高安全或临时通信场景。
  • 示例路径:"\0hidden_socket"

9. 总结

关键点说明
定义Unix Socket 是一种本地进程间通信机制,基于文件路径寻址
性能比 TCP/UDP 更快,延迟最低
类型流式(SOCK_STREAM)、数据报(SOCK_DGRAM)、原始(SOCK_RAW)
典型应用/dev/log、Docker、MySQL、systemd journald
与网络协议对比无需 IP 协议栈,只能在同一主机使用

总结结论:
Unix Socket 是 Linux 系统中高效、稳定、广泛使用的 IPC 机制,尤其在日志系统、容器管理、数据库通信等场景中不可替代。