如何在Swift中使用UNIX domain sockets实现IPC进程间通信
基于UNIX Domain Socket的Swift IPC实现教程
核心逻辑
UNIX Domain Socket是专门用于本地进程间通信的socket实现,不需要走网络栈,性能更高,核心流程非常简单:
- 服务端流程:创建socket → 绑定到本地文件路径 → 监听连接请求 → 接收客户端连接 → 读写数据
- 客户端流程:创建socket → 连接到服务端绑定的文件路径 → 读写数据
- 注意:UNIX Domain Socket绑定的文件路径最大长度限制为104字节,尽量放在
/tmp这类短路径目录下,服务端启动前必须删除上次运行残留的socket文件,否则绑定会报错。
Swift实现不需要引入任何第三方库,直接调用系统内置的BSD Socket接口即可,import Darwin后就能用所有相关接口。
最简可运行实现
服务端代码
import Darwin // 自定义socket存储路径,不要超过104字节 let socketPath = "/tmp/my_swift_ipc_socket" // 先删除旧的socket文件,避免绑定失败 unlink(socketPath) // 创建socket:AF_UNIX代表本地域,SOCK_STREAM代表可靠流传输 let serverFD = socket(AF_UNIX, SOCK_STREAM, 0) guard serverFD != -1 else { fatalError("创建socket失败") } // 配置socket地址信息 var addr = sockaddr_un() addr.sun_len = UInt8(MemoryLayout<sockaddr_un>.size) // MacOS必填字段 addr.sun_family = sa_family_t(AF_UNIX) _ = withUnsafeMutableBytes(of: &addr.sun_path) { pathPtr in socketPath.withCString { cStr in strcpy(pathPtr.baseAddress?.assumingMemoryBound(to: CChar.self), cStr) } } let addrLen = socklen_t(MemoryLayout<sockaddr_un>.size) // 绑定socket到指定路径 let bindResult = withUnsafePointer(to: &addr) { addrPtr in addrPtr.withMemoryRebound(to: sockaddr.self, capacity: 1) { sockAddrPtr in bind(serverFD, sockAddrPtr, addrLen) } } guard bindResult != -1 else { fatalError("绑定socket失败,请检查路径权限") } // 启动监听,最大支持5个待处理连接 let listenResult = listen(serverFD, 5) guard listenResult != -1 else { fatalError("启动监听失败") } print("服务端启动成功,等待客户端连接...") // 阻塞等待客户端连接 let clientFD = accept(serverFD, nil, nil) guard clientFD != -1 else { fatalError("接收客户端连接失败") } print("客户端已接入") // 读取客户端发送的消息 var buffer = [CChar](repeating: 0, count: 1024) let readLength = read(clientFD, &buffer, 1024) if readLength > 0 { let receivedMsg = String(cString: buffer) print("收到客户端消息:\(receivedMsg)") // 给客户端返回响应 let response = "服务端已收到消息:\(receivedMsg)" response.withCString { cStr in write(clientFD, cStr, strlen(cStr)) } } // 关闭连接,清理socket文件 close(clientFD) close(serverFD) unlink(socketPath)
客户端代码
import Darwin let socketPath = "/tmp/my_swift_ipc_socket" // 创建socket let clientFD = socket(AF_UNIX, SOCK_STREAM, 0) guard clientFD != -1 else { fatalError("创建socket失败") } // 配置服务端地址 var addr = sockaddr_un() addr.sun_len = UInt8(MemoryLayout<sockaddr_un>.size) addr.sun_family = sa_family_t(AF_UNIX) _ = withUnsafeMutableBytes(of: &addr.sun_path) { pathPtr in socketPath.withCString { cStr in strcpy(pathPtr.baseAddress?.assumingMemoryBound(to: CChar.self), cStr) } } let addrLen = socklen_t(MemoryLayout<sockaddr_un>.size) // 连接服务端 let connectResult = withUnsafePointer(to: &addr) { addrPtr in addrPtr.withMemoryRebound(to: sockaddr.self, capacity: 1) { sockAddrPtr in connect(clientFD, sockAddrPtr, addrLen) } } guard connectResult != -1 else { fatalError("连接服务端失败,请确认服务端已启动且路径正确") } // 发送消息 let sendMsg = "Hello IPC from Swift Client" sendMsg.withCString { cStr in write(clientFD, cStr, strlen(cStr)) } print("消息已发送") // 接收服务端返回 var buffer = [CChar](repeating: 0, count: 1024) let readLength = read(clientFD, &buffer, 1024) if readLength > 0 { let response = String(cString: buffer) print("收到服务端响应:\(response)") } // 关闭连接 close(clientFD)
运行方法
- 把服务端代码存为
Server.swift,执行swiftc Server.swift -o ipc_server编译,运行./ipc_server启动服务端 - 把客户端代码存为
Client.swift,执行swiftc Client.swift -o ipc_client编译,新开终端运行./ipc_client即可看到通信效果
进阶优化方向
你可以在跑通基础版本后逐步扩展功能:
- 用GCD的
DispatchSource监听文件描述符的可读/可写事件,把同步阻塞逻辑改成异步,避免卡住主线程 - 封装成独立的工具类,自动处理异常断开、自动重连、数据粘包拆分问题
- 需要传输复杂结构体时,可以把数据序列化成JSON、Protobuf格式后再发送
内容的提问来源于stack exchange,提问作者Tyler Smith
相关产品推荐
相关产品推荐

