| name | cangjie-socket |
| description | 仓颉语言 Socket 编程。当需要了解仓颉语言的TCP/UDP Socket编程、Socket选项与超时、Unix Domain Socket、IP地址工具、TLS/SSL加密通信等特性时,应使用此 Skill。 |
仓颉语言 Socket 编程 Skill
1. 网络概述
1.1 分层
- 传输层(
std.net 包):TCP(TcpSocket)、UDP(UdpSocket)、Unix Domain Socket
- 安全层(
stdx.net.tls 包):TLS 1.2/1.3 加密传输
1.2 关键规则
- 网络操作在仓颉线程级别是阻塞的,但不阻塞 OS 线程(仓颉线程让出)
- 所有 Socket 均实现
Resource,使用 try-with-resource 自动清理
1.3 地址类型
SocketAddress(抽象基类)→ IPSocketAddress(IP+端口)、UnixSocketAddress(文件路径)
IPAddress(抽象)→ IPv4Address、IPv6Address
IPAddress.parse(str) / IPAddress.tryParse(str) — 解析地址
IPAddress.resolve(hostname) — DNS 解析
- 常用判断:
isLoopback()、isPrivate()、isMulticast()
2. TCP 编程
2.1 服务端
TcpServerSocket(bindAt: port) → bind() → accept()(阻塞等待连接)
accept(timeout) — 带超时的接受连接
- 属性:
backlogSize、reuseAddress、reusePort、receiveBufferSize、sendBufferSize
2.2 客户端
TcpSocket(host, port) → connect() → read()/write()
connect(timeout) — 带超时连接
- 超时配置:
readTimeout、writeTimeout(Duration 类型)
- TCP 调优:
noDelay(TCP_NODELAY)、keepAlive(SocketKeepAliveConfig)、linger
2.3 完整示例
import std.net.*
import std.sync.*
let SERVER_PORT: UInt16 = 33333
let ready = SyncCounter(1)
func runServer() {
try (server = TcpServerSocket(bindAt: SERVER_PORT)) {
server.bind()
ready.dec()
try (client = server.accept()) {
let buf = Array<Byte>(10, repeat: 0)
let n = client.read(buf)
println("Server read ${n} bytes: ${buf}")
}
}
}
main(): Int64 {
let fut = spawn { runServer() }
ready.waitUntilZero()
try (socket = TcpSocket("127.0.0.1", SERVER_PORT)) {
socket.connect()
socket.write([1, 2, 3])
}
fut.get()
return 0
}
3. UDP 编程
UdpSocket(bindAt: port) → bind()
- 发送:
sendTo(IPSocketAddress, data) 或连接后 send(data)
- 接收:
receiveFrom(buffer) → (SocketAddress, count),或连接后 receive(buffer)
- 可选
connect(addr) 锁定远端地址(之后可用 send/receive)
disconnect() — 解除连接
- 限制:单包最大 64KB
- 超时:
receiveTimeout、sendTimeout
import std.net.*
import std.sync.*
import std.time.*
let SERVER_PORT: UInt16 = 33333
let barrier = Barrier(2)
func runUdpServer() {
try (server = UdpSocket(bindAt: SERVER_PORT)) {
server.bind()
barrier.wait()
let buf = Array<Byte>(3, repeat: 0)
let (addr, n) = server.receiveFrom(buf)
println("Received ${n} bytes: ${buf}")
}
}
main(): Int64 {
let fut = spawn { runUdpServer() }
barrier.wait()
try (sock = UdpSocket(bindAt: 0)) {
sock.sendTimeout = Duration.second * 2
sock.bind()
sock.sendTo(IPSocketAddress("127.0.0.1", SERVER_PORT), [1, 2, 3])
}
fut.get()
return 0
}
4. Socket 选项与超时
4.1 通用选项
| 属性 | 说明 |
|---|
readTimeout / writeTimeout | 读写超时(Duration 类型),超时抛 SocketTimeoutException |
reuseAddress / reusePort | 地址/端口复用 |
receiveBufferSize / sendBufferSize | 收发缓冲区大小 |
4.2 TCP 专有
| 属性 | 说明 |
|---|
noDelay | 禁用 Nagle 算法(降低延迟) |
keepAlive | SocketKeepAliveConfig(interval, count) — TCP 保活配置 |
linger | SO_LINGER — 关闭时等待数据发送完毕 |
4.3 底层选项访问
getSocketOptionIntNative(level, name) / setSocketOptionIntNative(level, name, value)
OptionLevel:TCP、SOCKET 等
SocketOptions:TCP_NODELAY、SO_KEEPALIVE、SO_REUSEADDR 等常量
import std.net.*
import std.time.*
main() {
try (sock = TcpSocket("127.0.0.1", 80)) {
sock.readTimeout = Duration.second
sock.noDelay = true
sock.linger = Duration.minute
sock.keepAlive = SocketKeepAliveConfig(
interval: Duration.second * 7,
count: 15
)
}
}
5. Unix Domain Socket
- 基于文件路径的进程间通信(IPC),不经过网络栈
- 不支持 Windows,路径最大 108 字节
- 流式:
UnixServerSocket(bindAt: path) + UnixSocket(path)
- 数据报式:
UnixDatagramSocket(bindAt: path)
- 使用后需手动
remove(path) 清理 socket 文件
import std.net.*
import std.sync.*
import std.fs.*
let SOCK_PATH = "/tmp/cj_demo.sock"
let barrier = Barrier(2)
func runServer() {
try (server = UnixServerSocket(bindAt: SOCK_PATH)) {
server.bind()
barrier.wait()
try (client = server.accept()) {
client.write("hello".toArray())
}
}
}
main(): Int64 {
let fut = spawn { runServer() }
barrier.wait()
try (sock = UnixSocket(SOCK_PATH)) {
sock.connect()
let buf = Array<Byte>(5, repeat: 0)
sock.read(buf)
println(String.fromUtf8(buf)) // "hello"
}
fut.get()
remove(SOCK_PATH)
return 0
}
6. 异常类型
| 异常 | 说明 |
|---|
SocketException | 通用 Socket 错误 |
SocketTimeoutException | Socket 操作超时 |
7. 关键规则速查
- 所有 Socket/Server 使用
try-with-resource 自动清理
- TCP 服务端模式:
TcpServerSocket → bind() → 循环 accept()
- UDP 单包最大 64KB
- TLS 需要先建立 TCP 连接,再在其上创建
TlsSocket 并 handshake()