Solod v0.2:网络支持、新目标、更友好的互操作

Lobsters Hottest 工具

摘要

Solod v0.2 发布,新增网络支持(TCP、UDP、Unix 套接字)、新的编译目标(32位、WebAssembly、裸机)以及改进的 C 互操作性。

<p><a href="https://lobste.rs/s/glklp0/solod_v0_2_networking_new_targets">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/06/29 20:31

# Solod v0.2:网络、新目标、更友好的互操作 来源:https://antonz.org/solod-v0-2/ Solod(**So**)是一门系统级语言,采用 Go 语法、零运行时,并拥有熟悉的 Go 标准库。它主要面向两类开发者: - 希望获得底层控制和零成本的 C 语言互操作,但不想学习 Zig 或 Odin 的 Go 开发者。 - 喜欢 Go 风格的 C 开发者。 [上一个版本](https://antonz.org/solod-v0-1)(v0.1)专注于移植 Go 核心 stdlib 包,并提供便捷的 C 语言互操作。在那篇文章的结尾,我提到下一个版本将专注于网络、并发,或者两者兼顾。现在,网络功能已经到来——我今天分享的 v0.2 版本包含了对 TCP、UDP 和 Unix 域套接字的支持。并发仍计划在将来实现,因此目前服务器一次只处理一个连接。 这个版本还允许你将 So 编译到更多目标平台,比如 32 位平台、WebAssembly 和裸机。而且 C 语言互操作更加流畅! - [网络](https://antonz.org/solod-v0-2/#networking) - [TCP 服务器](https://antonz.org/solod-v0-2/#tcp-server) - [TCP 客户端](https://antonz.org/solod-v0-2/#tcp-client) - [截止时间](https://antonz.org/solod-v0-2/#deadlines) - [IP 地址](https://antonz.org/solod-v0-2/#ip-addresses) - [目标平台](https://antonz.org/solod-v0-2/#new-targets) - [互操作](https://antonz.org/solod-v0-2/#friendlier-interop) - [标准库](https://antonz.org/solod-v0-2/#more-stdlib) - [总结](https://antonz.org/solod-v0-2/#wrapping-up) ## 网络 v0.2 的主要特性是 `net` 包。它是 Go 语言 `net` 包的简化版本,支持三种最常用的传输协议: - **TCP**(网络类型 `tcp`、`tcp4`、`tcp6`),通过 `ResolveTCPAddr`、`DialTCP` 和 `ListenTCP`,以及 `TCPConn` 和 `TCPListener` 类型。 - **UDP**(网络类型 `udp`、`udp4`、`udp6`),通过 `ResolveUDPAddr`、`DialUDP`(连接套接字)和 `ListenUDP`(未连接套接字,使用 `ReadFrom`/`WriteTo`)。 - **Unix 域套接字**(流使用 `unix`,数据报使用 `unixgram`),通过 `ResolveUnixAddr`、`DialUnix`、`ListenUnix` 和 `ListenUnixgram`。 API 与 Go 非常相似,因此大部分内容会让你感觉熟悉。最大的区别是 So 没有 goroutine,因此不支持并发服务器——你需要顺序地接受和提供连接。稍后会详细介绍。 ## TCP 服务器 我们来构建一个经典:一个回显服务器,接受连接,读取消息,然后将其发送回去。 ```go package main import "solod.dev/so/net" func main() { // 解析要监听的本地地址。 laddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:8080") if err != nil { panic(err) } // 开始在本地地址上监听。 ln, err := net.ListenTCP("tcp", &laddr) if err != nil { panic(err) } defer ln.Close() println("listening on", "127.0.0.1:8080") // 接受连接并在循环中提供服务。 for { conn, err := ln.Accept() if err != nil { panic(err) } serve(&conn) } } // serve 从连接中读取一条消息,将其回显,然后关闭连接。 func serve(conn *net.TCPConn) { defer conn.Close() var buf [256]byte n, err := conn.Read(buf[:]) if err != nil { return } conn.Write(buf[:n]) } ``` ``` listening on 127.0.0.1:8080 ``` 如果你用 Go 写过 TCP 服务器,这看起来应该很熟悉——`ListenTCP`、`Accept` 循环,以及连接上的 `Read`/`Write`。唯一缺少的是 `go serve(conn)`:没有 goroutine,每个连接必须完全处理完毕后才能继续下一个 `Accept`。 ## TCP 客户端 客户端使用 `DialTCP` 建立连接,然后使用 `Write` 发送请求,使用 `Read` 获取回复: ```go package main import "solod.dev/so/net" func main() { // 解析服务器地址。 raddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:8080") if err != nil { panic(err) } // nil laddr 让系统选择本地地址。 conn, err := net.DialTCP("tcp", nil, &raddr) if err != nil { panic(err) } defer conn.Close() // 发送请求并读取回复。 conn.Write([]byte("hello")) var buf [256]byte n, err := conn.Read(buf[:]) if err != nil { panic(err) } println(string(buf[:n])) } ``` UDP 和 Unix 域套接字的工作方式类似。对于 UDP,未连接的 `ListenUDP` 套接字使用 `ReadFrom` 获取数据和发送者地址,使用 `WriteTo` 发送回复。对于 Unix 套接字,有 `ListenUnix`(流)和 `ListenUnixgram`(数据报)。 ## 截止时间 默认情况下,`Accept`、`Read` 和 `Write` 是阻塞的。在 Go 中,你通常使用 goroutine 和 context 来防止永久卡住。由于 So 尚不支持这些,每个连接和监听器都支持截止时间: ```go // 给客户端 5 秒时间发送数据。 conn.SetReadDeadline(time.Now().Add(5 * time.Second)) n, err := conn.Read(buf[:]) if err == net.ErrTimeout { // 客户端没有响应,断开连接。 return } ``` `SetDeadline`、`SetReadDeadline` 和 `SetWriteDeadline` 在 `TCPConn`、`UDPConn`、`UnixConn` 以及监听器类型上可用。当截止时间过去时,任何挂起的调用都会失败并返回 `net.ErrTimeout`。如果你不设置截止时间,阻塞调用将永远等待。这不是并发,但足以让单线程服务器保持响应。 ## IP 地址 除了 `net`,v0.2 还移植了 Go 的 `net/netip` 包,该包为 IP 地址提供小巧、零分配的值类型。`Addr` 表示 IP 地址,`AddrPort` 将 IP 地址与端口结合,`Prefix` 是带前缀长度(CIDR 块)的 IP: ```go addr, err := netip.ParseAddr("192.168.1.10") if err != nil { panic(err) } println(addr.Is4()) // true ap := netip.AddrPortFrom(addr, 8080) println(ap.Port()) // 8080 prefix := netip.MustParsePrefix("192.168.1.0/24") println(prefix.Contains(addr)) // true ``` 这些是简单的值类型,不进行任何堆分配,非常符合 So 显式内存管理的理念。`net` 包还提供了 `SplitHostPort` 和 `JoinHostPort` 函数,帮助你处理 `host:port` 字符串。 ## 新目标 Solod 编译为纯 C,这理论上意味着它可以针对 C 编译器能处理的任何目标。因此,v0.2 增加了新目标: - **32 位平台**。编译器和 stdlib 现在能在 32 位平台上正确工作,这些平台上 `int` 和指针更窄。 - **WebAssembly(WASI)**。你可以将 So 程序编译为 `wasm32-wasi`,并在任何 WASI 运行时下运行。 - **独立模式**。So 程序可以在没有 C 标准库的裸机系统上运行。没有 libc 意味着没有 malloc,但你可以使用 `mem.Arena` 代替。 以下是使用 `zig cc` 构建独立 `wasm32` 二进制文件所需的完整工具链: ```bash export CC="zig cc" export CFLAGS="-Oz --target=wasm32-freestanding -nostdlib -Wl,--no-entry -Wl,--export=main" so build -o main.wasm . ``` 标准库的大部分内容(`bytes`、`strings`、`strconv`、`slices`、`maps`、`math`、`encoding/binary` 等)在独立模式下都能正常工作。更多详情,请查看[独立模式指南](https://github.com/solod-dev/solod/blob/main/doc/freestanding.md)。 ## 更友好的互操作 一系列较小的改动让 Solod 编写起来更舒心。 **三个新的指令**,用于底层工作,全部在[互操作指南](https://github.com/solod-dev/solod/blob/main/doc/interop.md)中有文档说明: ```go //so:volatile var counter int // 生成 C volatile //so:thread_local var perThread int // 生成 C11 _Thread_local //so:attr packed type header struct { // 生成 __attribute__((packed)) version byte length int } ``` `so:attr` 可用于变量、常量、类型和函数。你可以在多行中使用它,属性会累积。例如,`//so:attr aligned(16)` 会与 `//so:attr packed` 组合。 **类型别名**。So 现在支持 Go 风格的类型别名: **数值 C 类型**。`so/c` 包现在包含了 C 数值类型的命名类型——`Int`、`UInt`、`Long`、`Short`、`UChar`、`LongLong` 等。当你声明外部函数时,可以在签名中使用实际的 C 类型,而不必猜测平台上正确的固定宽度 Go 类型。 **第三方包**。你现在可以使用 `go install` 或 vendoring 添加外部 So 包,并且可以将自己的代码组织成多个模块。So 还没有真正的包生态系统,但这是一个好的开始。 **更好的诊断**。默认情况下,恐慌消息报告的是 C 文件和行号。使用 `--track-source` 标志可以报告原始的 So 源位置: 还有一个可选的 `--check-nil` 标志,在访问结构体字段和调用接口方法时会添加空指针检查。这样,如果出现错误的解引用,程序会干净地恐慌,而不是导致段错误。这两个选项默认关闭,以保持生成的代码更易读。 ## 更多标准库 除了 `net` 和 `net/netip`,v0.2 还增加了一些包: - `encoding/hex`——十六进制编码和解码,包括用于十六进制转储样式输出的 `Dump`。 - `uuid`——生成和解析 UUID(v4 和 v7),随机分量来自加密安全的源。 还有一个虽小但实用的内存管理更新:`mem.Arena.Free` 现在如果传入匹配的指针,可以回收上次分配的内存。这是一个小的优化,但意味着 arena 上的快速分配/释放对不再浪费空间。 [标准库文档](https://github.com/solod-dev/solod/blob/main/doc/stdlib.md) ## 总结 随着 v0.2 的发布,Solod 已从仅仅用于“命令行工具和 C 粘合代码”发展为你实际可以在网络上使用的语言——比如 TCP 或 UDP 服务器、小型协议客户端或 Unix 套接字守护进程。新的目标平台(32 位、WASM、独立)意味着相同的代码现在可以在更多地方运行,甚至向下到裸机。 仍然缺少的主要功能是并发。一次处理一个请求的服务器适用于某些任务,但真正的网络服务需要同时管理许多连接。这是 v0.3 的明确目标——添加某种形式的并发,以及支持它的 stdlib 包。 如果你感兴趣,可以查看 So 的[自述文件](https://github.com/solod-dev/solod#readme)——它包含开始使用所需的一切。或者[在线尝试 So](https://codapi.org/so),无需安装任何东西。 ★ [订阅](https://antonz.org/subscribe/) 以获取最新文章。

相似文章

Better Sol

Product Hunt

Better Sol 是一款工具,使开发者能够使用 TypeScript 构建端到端的 Solana 应用程序。

Zig ELF 链接器改进开发日志

Hacker News Top

新的 Zig ELF 链接器现在支持外部库和 C 源码的快速增量编译,在 x86_64 Linux 上能够实现毫秒级重建。

QSOE: 受QNX启发的双内核架构操作系统

Hacker News Top

QSOE项目v0.1发布,提供与QNX兼容的操作系统,包含两种微内核变体(自定义Skimmer内核和基于seL4的内核),共享用户空间,并支持SiFive Unmatched RISC-V硬件。

Stalwart v0.16:全新基石

Lobsters Hottest

Stalwart v0.16 是一个重大版本,对架构进行了彻底重建,带来了全新的 WebUI、以基于 JMAP 的统一管理替代 REST API、新增外部 OIDC 认证支持,以及用于管理和基础设施即代码工作流的新 CLI 工具。