Cangjie 标准库 std.net 实战:用 UnixDatagramSocket 实现进程间数据报通信
人工智能AI 技能文档开发工具【免费下载链接】DocAgent项目地址https://gitcode.com/Cangjie-SIG/DocAgent点击查看免费下载导读本文聚焦 Cangjie 标准库std.net中的UnixDatagramSocketUnix 域数据报套接字通过一个完整的可运行示例讲解如何在同一台主机上的进程之间以数据报datagram形式收发报文。读完本文你将掌握UnixDatagramSocket的创建、绑定、连接、发送与接收的完整调用流程理解它与 UDP、TCP 及面向流的UnixSocket的差异并能据此编写自己的进程间通信程序。示例源码位于仓库 std/net/net_samples/unix_datagram.md对应的完整类 API 说明见 UnixDatagramSocket 类文档。一、UDS 与 UnixDatagramSocket 是什么1.1 UDS本机进程间通信的高效通道Unix Domain SocketUDSUnix 域套接字是用于在同一台计算机上的进程之间通信的机制。与网络套接字不同UDS 不需要网络协议栈和网络设备因此通信更快、延迟更低、吞吐量更高。它通过文件系统路径而不是 IP 地址和端口号来标识通信端点。std.net包同时支持 UDP / TCP / UDS 三种 Socket 类型。其中UnixDatagramSocket属于数据报datagram类别与之对应的还有面向流的UnixSocket流式客户端与UnixServerSocket流式服务端。从包概览中的类继承关系可以看出整体结构Resource ├StreamingSocket │ ├TcpSocket │ └UnixSocket ├DatagramSocket │ ├UdpSocket │ └UnixDatagramSocket └ServerSocket ├TcpServerSocket └UnixServerSocket继承关系见 std/net/net_package_overview.md1.2 UnixDatagramSocket 的特点按照类文档的描述UnixDatagramSocket提供基于数据包的主机通讯能力其关键特性包括创建后需显式bind()实例创建后应当显式调用bind()接口绑定到本地文件路径无连接或可选连接Unix 数据包套接字本身不需要连接、不需要与远端多次握手但用户也可以通过connect/disconnect接口与远端建连和断连无固定数据包大小上限不同于 UDP一次最多处理 64KB 数据包UDS 没有数据包大小限制限制来源于操作系统和接口实现资源需显式回收套接字资源需要用close接口显式释放平台限制该类型不支持在 Windows 平台上运行。数据报传输本身不是可靠的传输不保证传输顺序数据包大小在发送端决定一端发送了 10 字节和 15 字节的报文对端将收到相同大小的对应报文相关语义见 DatagramSocket 接口。二、完整示例服务端与客户端的数据报收发下面直接给出仓库中的完整示例见 unix_datagram.md。它演示了一个典型场景客户端向服务端发送 3 字节报文[1, 2, 3]服务端校验接收内容与客户端地址。import std.net.* import std.sync.* import std.fs.* import std.random.* import std.env.* let barrier Barrier(2) func createTempFile(): String { let tempDir: Path getTempDirectory() let index: String Random().nextUInt64().toString() return tempDir.join(tmp${index}).toString() } func runUnixDatagramServer(serverPath: String, clientPath: String) { try (serverSocket UnixDatagramSocket(bindAt: serverPath)) { serverSocket.bind() barrier.wait() let buf ArrayByte(3, repeat: 0) let (clientAddr, read) serverSocket.receiveFrom(buf) if (read 3 buf [1, 2, 3]) { println(server received) } if (clientAddr.toString() clientPath) { println(client address correct) } } } main(): Int64 { let clientPath createTempFile() let serverPath createTempFile() let fut spawn { runUnixDatagramServer(serverPath, clientPath) } barrier.wait() try (unixSocket UnixDatagramSocket(bindAt: clientPath)) { unixSocket.sendTimeout Duration.second * 2 unixSocket.bind() unixSocket.connect(serverPath) unixSocket.send([1, 2, 3]) } fut.get() return 0 }运行结果server received client address correct三、逐段拆解示例的调用流程3.1 准备为两端生成独立的套接字文件路径示例先通过createTempFile()生成两个唯一的临时路径分别作为服务端serverPath与客户端clientPath的套接字文件路径func createTempFile(): String { let tempDir: Path getTempDirectory() let index: String Random().nextUInt64().toString() return tempDir.join(tmp${index}).toString() }其思路是利用std.random的Random().nextUInt64()生成随机数拼接为文件名放在系统临时目录getTempDirectory()来自std.env下从而避免与已有文件冲突——因为bind()要求目标路径当前不存在路径已存在会直接绑定失败。3.2 服务端创建、绑定并阻塞接收func runUnixDatagramServer(serverPath: String, clientPath: String) { try (serverSocket UnixDatagramSocket(bindAt: serverPath)) { serverSocket.bind() barrier.wait() let buf ArrayByte(3, repeat: 0) let (clientAddr, read) serverSocket.receiveFrom(buf) ... } }几个关键点构造函数UnixDatagramSocket(bindAt: serverPath)创建未连接的实例。构造函数接受bindAt!: String文件路径或bindAt!: SocketAddress套接字地址两种形式且要求地址当前不存在在bind时会创建对应的套接字文件见 net_package_classes.md 中init(String)/init(SocketAddress)bind()显式绑定套接字并创建监听队列。bind会自动在本地地址创建一个套接字文件若文件已存在则绑定失败失败后需要close套接字不支持多次重试。该文件可通过 POSIX 的isSock()判断是否存在、用unlink()删除receiveFrom(buf)阻塞式收取报文。返回值是(SocketAddress, Int64)二元组——报文发送端地址与实际收取到的报文大小。注意buf需要有合适的大小否则报文会被截断且返回值可能大于buf大小读取超时会抛出SocketTimeoutException。服务端收到的报文来自已bind的客户端套接字因此clientAddr.toString()应与clientPath相等——这正是示例中第二个断言所验证的内容。3.3 客户端创建、绑定、连接并发送try (unixSocket UnixDatagramSocket(bindAt: clientPath)) { unixSocket.sendTimeout Duration.second * 2 unixSocket.bind() unixSocket.connect(serverPath) unixSocket.send([1, 2, 3]) }sendTimeout设置send/sendTo操作超时时间。这里设为Duration.second * 22 秒。超时时间过小会被设置为最小时钟周期值过大则设置为最大超时时间2^63-1 纳秒默认值为None不超时设为负数会抛出IllegalArgumentExceptionbind()客户端同样需要先绑定自己的地址这样服务端才能通过receiveFrom拿到客户端的地址connect(remotePath: String)连接特定远端地址即服务端路径。该重载必须在bind后调用连接后仅接受来自该远端地址的报文。类文档同时提示连接后端口开始接收 ICMP 报文若收到异常报文可能导致后续send/sendTo执行失败send([1, 2, 3])将报文发送到connect连接的地址。若payload大小超出系统限制或系统发送失败会抛出SocketException。值得注意的是示例中客户端使用了connect send的组合面向单一对端的快捷路径。UnixDatagramSocket同样支持无连接的sendTo(recipient: SocketAddress, payload)向任意对端发送以及receive从已连接地址收取报文。disconnect()可撤销连接配置它允许在connect前调用、也允许多次调用。3.4 同步与并发控制示例用std.sync的Barrier(2)让主线程与spawn出来的服务端协程/线程互相等待服务端在bind()后barrier.wait()主线程在spawn后barrier.wait()两边都到达屏障后才继续从而保证「先绑定服务端、再启动客户端」的执行顺序避免客户端发送时报文无处可收最后通过fut.get()等待服务端协程执行完毕再返回0。3.5 资源管理try-with-resources 自动关闭两端均使用try (sock UnixDatagramSocket(...)) { ... }的写法退出作用域时自动调用close()释放套接字。close之后除close/isClosed外的所有操作均不允许再调用该接口允许多次调用见 UnixDatagramSocket 类文档。四、与 UDP、面向流 UnixSocket 的对比4.1 UnixDatagramSocket vs UdpSocket两者都实现DatagramSocket接口receiveFrom/sendTo/localAddress/remoteAddress/receiveTimeout/sendTimeout但差异明显维度UdpSocketUnixDatagramSocket通信范围跨主机IP 端口仅本机文件系统路径地址形式IPSocketAddress(127.0.0.1, port)文件路径字符串 /SocketAddress数据包大小最大约 64KB无固定限制取决于操作系统与实现运行平台跨平台不支持 Windows作为参照UDP 示例见 udp.md其服务端用UdpSocket(bindAt: SERVER_PORT)receiveFrom收包客户端用UdpSocket(bindAt: 0)随机端口sendTo(IPSocketAddress(...), [1, 2, 3])发包。与 UnixDatagramSocket 示例对比可以看出数据报套接字的「绑定—接收 / 绑定—发送」骨架是一致的差异主要体现在地址的构造方式上。4.2 UnixDatagramSocket vs UnixSocketUnixSocket是面向连接的流式套接字继承StreamingSocket需要通过accept()/connect()建立连接后以流的方式read/write适合连续字节流场景而UnixDatagramSocket以报文为边界每条报文独立收发、大小由发送端决定。流式 UDS 的完整示例见 unix.md其中服务端用UnixServerSocket监听、accept()后write(hello.toArray())客户端UnixSocket(SOCKET_PATH)连接后read读取——收发模式与数据报有明显区别。选择哪一种取决于应用是「消息边界明确、可容忍丢失的数据报」还是「需要可靠有序字节流」。五、使用要点与注意事项路径唯一性bind()前目标文件路径必须不存在否则抛出SocketException绑定失败后需close不能重试。生产代码可用isSock()检查、unlink()清理残留的套接字文件相关函数见 POSIX 包文档。先 bind 再 connectconnect(remotePath: String)必须在bind之后调用若使用connect(remote: SocketAddress)重载则默认执行bind无需额外调用。超时配置sendTimeout/receiveTimeout默认None无限等待可设置为Duration为负值抛出IllegalArgumentException。示例中设置 2 秒发送超时是一种避免长期阻塞的实用做法。缓冲大小receiveFrom的buffer应足够大以容纳完整报文否则报文会被截断返回值大于 buffer 长度可通过receiveBufferSize/sendBufferSize调整SO_RCVBUF/SO_SNDBUF具体生效情况取决于系统。平台与异常UnixDatagramSocket不支持 Windows收发失败、超时会分别抛出SocketException、SocketTimeoutException见 net 包异常文档。资源回收务必通过close()或try表达式显式关闭套接字释放系统资源。六、进一步阅读类完整 API构造函数、bind/connect/disconnect/send/sendTo/receive/receiveFrom 及各属性UnixDatagramSocket 类文档数据报套接字通用接口语义DatagramSocket 接口std.net包整体架构与 Socket 继承关系net 包概览同类对比示例UDP 使用示例、UNIX流式使用示例、TCP 使用示例、Socket 选项使用示例赞分享人工智能AI 技能文档开发工具【免费下载链接】DocAgent项目地址https://gitcode.com/Cangjie-SIG/DocAgent点击查看免费下载相关推荐仓颉 std.net 的 UnixDatagramSocket 详解基于 UDS 数据报的进程间通信实战仓颉 std.net 的 UnixDatagramSocket 详解基于 UDS 数据报的进程间通信实战 Unix Domain SocketUDS数据报人工智能AI 技能文档开发工具仓颉 std.net UnixDatagramSocket 使用指南基于 Unix 域数据报套接字的进程间通信实战仓颉 std.net UnixDatagramSocket 使用指南基于 Unix 域数据报套接字的进程间通信实战 本篇技术指南以仓颉编程语言运行时与标准库编程语言语言运行时标准库垃圾回收并发编程DocAgent 标准库 std.net 实战Cangjie IPv6Address 类全解析与编程指南DocAgent 标准库 std.net 实战Cangjie IPv6Address 类全解析与编程指南 本文以 std.net https://link.g人工智能AI 技能文档开发工具上一篇Egg 框架目录结构约定从项目骨架到 Loader 加载原理下一篇awc (Actix Web Client) 实战指南异步 HTTP 与 WebSocket 客户端开发创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考