news 2026/10/15 11:50:35

Cangjie 标准库 std.net 实战:用 UnixDatagramSocket 实现进程间数据报通信

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Cangjie 标准库 std.net 实战:用 UnixDatagramSocket 实现进程间数据报通信
  • 人工智能
  • AI 技能
  • 文档
  • 开发工具

【免费下载链接】DocAgent

项目地址:https://gitcode.com/Cangjie-SIG/DocAgent
点击查看免费下载

导读

本文聚焦 Cangjie 标准库std.net中的UnixDatagramSocket(Unix 域数据报套接字),通过一个完整的可运行示例,讲解如何在同一台主机上的进程之间以数据报(datagram)形式收发报文。读完本文,你将掌握UnixDatagramSocket的创建、绑定、连接、发送与接收的完整调用流程,理解它与 UDP、TCP 及面向流的UnixSocket的差异,并能据此编写自己的进程间通信程序。

示例源码位于仓库 std/net/net_samples/unix_datagram.md,对应的完整类 API 说明见 UnixDatagramSocket 类文档。

一、UDS 与 UnixDatagramSocket 是什么

1.1 UDS:本机进程间通信的高效通道

Unix Domain Socket(UDS,Unix 域套接字)是用于在同一台计算机上的进程之间通信的机制。与网络套接字不同,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.md)

1.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 = Array<Byte>(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 = Array<Byte>(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 * 2(2 秒)。超时时间过小会被设置为最小时钟周期值,过大则设置为最大超时时间(2^63-1 纳秒),默认值为None(不超时);设为负数会抛出IllegalArgumentException;
  • bind():客户端同样需要先绑定自己的地址,这样服务端才能通过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 UnixSocket

UnixSocket是面向连接的流式套接字(继承StreamingSocket),需要通过accept()/connect()建立连接后以流的方式read/write,适合连续字节流场景;而UnixDatagramSocket以报文为边界,每条报文独立收发、大小由发送端决定。流式 UDS 的完整示例见 unix.md,其中服务端用UnixServerSocket监听、accept()后write("hello".toArray()),客户端UnixSocket(SOCKET_PATH)连接后read读取——收发模式与数据报有明显区别。选择哪一种,取决于应用是「消息边界明确、可容忍丢失的数据报」还是「需要可靠有序字节流」。

五、使用要点与注意事项

  1. 路径唯一性:bind()前目标文件路径必须不存在,否则抛出SocketException;绑定失败后需close,不能重试。生产代码可用isSock()检查、unlink()清理残留的套接字文件(相关函数见 POSIX 包文档)。
  2. 先 bind 再 connect:connect(remotePath: String)必须在bind之后调用;若使用connect(remote: SocketAddress)重载则默认执行bind,无需额外调用。
  3. 超时配置:sendTimeout/receiveTimeout默认None(无限等待),可设置为Duration;为负值抛出IllegalArgumentException。示例中设置 2 秒发送超时,是一种避免长期阻塞的实用做法。
  4. 缓冲大小:receiveFrom的buffer应足够大以容纳完整报文,否则报文会被截断(返回值大于 buffer 长度);可通过receiveBufferSize/sendBufferSize调整SO_RCVBUF/SO_SNDBUF,具体生效情况取决于系统。
  5. 平台与异常:UnixDatagramSocket不支持 Windows;收发失败、超时会分别抛出SocketException、SocketTimeoutException(见 net 包异常文档)。
  6. 资源回收:务必通过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
点击查看免费下载

相关推荐

上一篇:Egg 框架目录结构约定:从项目骨架到 Loader 加载原理
下一篇:awc (Actix Web Client) 实战指南:异步 HTTP 与 WebSocket 客户端开发

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 11:50:32

BOOTSTRA.386 实战:打造复古 DOS 风格的 Bootstrap 5.3 Navbar 导航栏

前端UI组件 【免费下载链接】BOOTSTRA.386 A vintage 1980s DOS inspired Twitter Bootstrap theme 项目地址&#xff1a; https://gitcode.com/gh_mirrors/bo/BOOTSTRA.386 点击查看 免费下载 本文以 BOOTSTRA.386 仓库中 v5.3.1 版本文档 navbar.md 为核心骨架&#xff0c;完…

作者头像 李华
网站建设 2026/10/15 11:50:20

CentOS YUM安装报错/dev/mapper/cl-root空间不足的根因与修复

1. 问题本质与典型场景还原你执行yum install或dnf install时&#xff0c;终端突然卡住&#xff0c;最后跳出一行红色报错&#xff1a;Error: Disk Requirements: At least XXX MB more space needed on the / filesystem.接着下面跟着一行更刺眼的提示&#xff1a;The error w…

作者头像 李华
网站建设 2026/10/15 11:50:10

SpringBoot+Vue全栈企业工资管理系统开发实践

1. 项目概述 企业工资管理系统是现代企业人力资源管理的核心模块之一。这个基于SpringBootVue的全栈项目&#xff0c;实现了从员工信息管理、考勤统计到薪资计算、报表生成的全流程数字化解决方案。我在实际开发中发现&#xff0c;相比传统Excel手工核算方式&#xff0c;这套系…

作者头像 李华
网站建设 2026/10/15 11:49:49

CTF杂项解题实战:从文件识别到流量取证的完整工具链

1. 杂项到底在考什么&#xff1a;从一道签到题说起很多人刚接触CTF比赛的时候&#xff0c;会把注意力全放在逆向、PWN、Web这些方向上&#xff0c;觉得杂项&#xff08;Misc&#xff09;就是送分题&#xff0c;随便做做就行。我刚开始也是这么想的&#xff0c;直到有一次比赛被…

作者头像 李华
网站建设 2026/10/15 11:49:32

RGB、HSV、HSI颜色模型详解:从原理到实战选型

上周有个朋友甩给我一串十六进制颜色值&#xff0c;说想在一排LED灯上复现出那种“很正的蓝”。我按RGB思路调了一整晚&#xff0c;从#1D4ED8调到#1E50E8&#xff0c;怎么看都不对&#xff1a;要么发紫&#xff0c;要么发灰。后来干脆换到HSV模式&#xff0c;只动了一下色相和一…

作者头像 李华
网站建设 2026/10/15 11:49:25

用CodeGraph打造代码图谱:从依赖分析到架构治理实战

做技术这些年&#xff0c;最让我头大的不是写新代码&#xff0c;而是接手别人留下来的“祖传代码库”。一个项目跑了好几年&#xff0c;文档早就过时&#xff0c;新人光读懂端到端流程就要花两周&#xff0c;改一个看似无害的底层接口&#xff0c;根本不知道哪些模块会被牵一发…

作者头像 李华