Unix Domain Socket 编程:字节流与数据报套接字详解
Unix Domain Socket 编程:字节流与数据报套接字详解
一、字节流套接字(SOCK_STREAM
)
1. 核心特性
• 可靠传输:提供类似 TCP 的有序、无丢失数据流,无记录边界。
• 全双工通信:支持双向数据传输,需通过 listen()
/accept()
建立连接。
• 适用场景:需高可靠性的连续数据交互(如文件传输、RPC 服务)。
2. 核心系统调用
(1)socket()
创建套接字
int socket(int domain, int type, int protocol);
• 参数:
• domain
:必须为 AF_UNIX
或 AF_LOCAL
。
• type
:设为 SOCK_STREAM
。
• protocol
:固定为 0
。
• 返回值:成功返回文件描述符,失败返回 -1
(如 EPROTONOSUPPORT
协议不支持)。
(2)bind()
绑定地址
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
• 地址结构:
struct sockaddr_un {
sa_family_t sun_family; // AF_UNIX
char sun_path[108]; // 文件路径或抽象名称
};
• 路径类型:
• 普通路径:如 /tmp/mysocket.sock
,需确保路径可写且无冲突。
• 抽象命名空间(Linux 特有):sun_path[0] = '\0'
,后续字符为抽象名(无需文件实体)。
• addrlen 计算:offsetof(struct sockaddr_un, sun_path) + strlen(name)
(抽象名需 +1)。
(3)listen()
监听队列
int listen(int sockfd, int backlog);
• backlog:最大等待连接数,建议设为 SOMAXCONN
。
• 错误处理:若队列满,后续连接请求直接返回 ECONNREFUSED
。
(4)accept()
接收连接
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
• addr 参数:通常设为 NULL
(客户端地址无意义)。
• 返回值:新套接字描述符专用于该连接。
(5)数据传输
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
• flags 标志:
• MSG_OOB
:带外数据(需双方支持)。
• MSG_DONTWAIT
:非阻塞模式。
二、数据报套接字(SOCK_DGRAM
)
1. 核心特性
• 保留消息边界:每次 send()
对应一次 recv()
,数据按报文独立接收。
• 无连接特性:无需建立连接,可直接发送数据。
• 可靠性:数据不丢失但无序(与 UDP 不同,Unix 域数据报本身可靠)。
• 适用场景:日志传输、实时状态广播等低延迟场景。
2. 核心系统调用
(1)socket()
创建套接字
int socket(int domain, int type, int protocol);
• type:设为 SOCK_DGRAM
。
(2)bind()
绑定地址
• 地址规则:同字节流套接字,但允许同一地址被多次绑定(需设置 SO_REUSEADDR
)。
(3)直接数据收发
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
• dest_addr:目标地址(必须已绑定)。
• src_addr:接收时可获取发送方地址。
(4)连接模式(可选)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
• 作用:绑定默认目标地址,后续可使用 send()
/recv()
以及write()
/read()
。
三、Linux 抽象 Socket 名空间(跨进程通用)
1. 实现方式
• 命名规则:sun_path[0] = '\0'
,后续字符为抽象名(如 \0myapp
)。
• 优势:
• 无文件系统依赖,避免路径权限问题。
• 进程退出后自动释放,无需手动清理。
2. 系统查询
ss -x -a | grep '@' # 查看活跃抽象套接字
cat /proc/net/unix # 内核级列表(含 inode 和权限)
四、进阶功能对比
特性 | 字节流套接字 | 数据报套接字 |
---|---|---|
连接管理 | 需 listen() /accept() | 无连接 |
记录边界 | 无边界(连续流) | 保留边界 |
最大消息长度 | 无限制 | 受限于内核缓冲区(通常 130KB) |
描述符传递 | 支持 (sendmsg() ) | 支持 (sendmsg() ) |
凭证传递 | 支持(SO_PASSCRED ) | 支持(SO_PASSCRED ) |
五、总结
字节流套接字适用于需要可靠连续数据流的场景(如数据库连接),而数据报套接字更适合短消息、无连接交互。Linux 抽象命名空间通过消除文件依赖提升了安全性和便捷性,尤其在容器化环境中优势显著。开发者应根据业务需求选择通信模式,并注意两者的系统调用差异。