# TCP 与 UDP
`net/*` 提供事件驱动的 TCP/UDP 服务器和出站客户端连接。所有 I/O
都是非阻塞的,运行在 VM 的 epoll 事件循环上——数据到达时通过回调
触发处理器。
服务器和连接是 `native_resource` 值:由 GC 跟踪、在终结器中关闭、
可从 REPL 动态启动和停止。
## `net/*` 标准库函数
| 函数 | 返回值 | 用途 |
|---|---|---|
| `[net/tcp $port $on-connect]` | 服务器资源 | 启动 TCP 服务器 |
| `[net/udp $port $on-data]` | 服务器资源 | 启动 UDP 服务器 |
| `[net/stop $server]` | `:ok` | 停止接受连接(保留资源) |
| `[net/start $server]` | `:ok` | 重新启动已停止的服务器 |
| `[net/close $server]` | `:ok` | 永久关闭并断开所有连接 |
| `[net/list]` | 列表 | 所有已注册的服务器 |
| `[net/info $server]` | 映射 | 服务器名称/端口/协议/状态/连接 |
| `[net/connections $server]` | 列表 | 活动连接 |
| `[net/connect $host $port]` | 连接资源 | 出站 TCP 客户端 |
| `[net/connect-tls $host $port]` | 连接资源 | 出站 TLS 客户端(mbedTLS) |
| `[net/send $conn $data]` | 已写入字节数或 `%Err` | 发送 |
| `[net/recv $conn]` | 字符串或 nil | 读取可用数据 |
| `[net/on-data $conn $fn]` | nil | 注册原始数据回调 |
| `[net/on-line $conn $fn]` | nil | 注册按行模式回调 |
| `[net/on-close $conn $fn]` | nil | 注册关闭回调 |
| `[net/close-conn $conn]` | nil | 关闭一个连接 |
| `[net/udp-send $server $addr $port $data]` | 字节数或 `%Err` | 发送 UDP 数据报 |
## TCP 服务器
def on-connect {|conn| @doc "Called with a fresh connection resource on each accept." echo "new connection from" $conn net/on-line $conn { |line| echo "received: $line" net/send $conn "echo: $line\n" } } def srv [net/tcp 8080 $on-connect] # Server runs on the event loop. Stop or close to clean up: # net/stop $srv # stop accepting, keep resource # net/close $srv # permanent close, drops all connections
连接处理器会收到一个代表该连接的 `native_resource`。在其上注册
`on-line`、`on-data` 和 `on-close` 回调来驱动协议。
## UDP 服务器
def on-data {|datagram| @doc "Called with each UDP datagram arrival." echo "got:" $datagram~data "from" $datagram~addr } def srv [net/udp 9090 $on-data] # To send back: use net/udp-send with the server, addr, and port # net/udp-send $srv "127.0.0.1" $client-port "reply\n"
UDP 是无连接的——没有按连接的资源,只有到达服务器的数据报。
## 客户端连接
def conn [net/connect "example.com" 80] net/send $conn "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n" set received "" net/on-data $conn { |data| set received ($received + $data) if [ends-with? $received "</html>"] { echo "got full response" net/close-conn $conn } } # For TLS: def tls-conn [net/connect-tls "api.github.com" 443]
## 回调
`net/on-data`、`net/on-line` 和 `net/on-close` 在连接上注册闭包。
再次调用即可替换。
net/on-data $conn { |data| echo "raw bytes: $data(len)" } net/on-line $conn { |line| echo "line: $line" } net/on-close $conn { echo "connection closed" }
`on-line` 按换行符拆分传入数据,每行触发一次。`on-data` 每次原始
读取触发一次。
## 服务器生命周期
def srv [net/tcp 8080 $on-connect] [net/info $srv] # => %{ :name "tcp:8080" :port 8080 :protocol :tcp :state :running :connections @{} } [net/connections $srv] # => @{ %{ :addr "127.0.0.1" :port 54321 } ... } net/stop $srv # stop accepting, keeps connections net/start $srv # start accepting again net/close $srv # permanent: stop + drop connections + free resource
## 服务器内省
[net/list]
# => @{ %{ :name "tcp:8080" :port 8080 :protocol :tcp :state :running } ... }
## TLS
出站 TLS 使用 mbedTLS。连接通过与普通 TCP 相同的按 fd 的 epoll
回调驱动一个小型状态机(连接 → 握手 → 就绪);就绪后,`net/send`
和 `net/on-line` 会透明地使用 `mbedtls_ssl_write` 和
`mbedtls_ssl_read`。
def c [net/connect-tls "api.github.com" 443] net/send $c "GET / HTTP/1.1\r\nHost: api.github.com\r\nConnection: close\r\n\r\n" net/on-line $c { |line| echo $line }
当前不支持服务器端 TLS。如果需要终止 TLS,请使用反向代理
(nginx、caddy)。
## 事件循环
`deft` 脚本二进制驱动单个事件循环。要处理传入连接和回调,脚本
必须持续运行——要么通过显式的 `vwait`(在协程上下文中),要么在
设置好服务器和回调后运行到脚本末尾。
def srv [net/tcp 8080 $on-connect] echo "listening on :8080" # script blocks here, event loop dispatches callbacks sleep 60000 # crude: keep alive 60s
在 TUI 应用中,工作区的应用线程循环会自动驱动事件循环。
## 示例
### 回显服务器
def on-connect {|conn| net/on-line $conn { |line| net/send $conn "echo: $line\n" } } def srv [net/tcp 8080 $on-connect] echo "echo server on :8080" sleep 600000
### HTTP 请求
def c [net/connect "example.com" 80] net/send $c "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n" set body "" net/on-data $c { |data| set body ($body + $data) } net/on-close $c { echo "got $($body(len)) bytes" }
## 快速参考
| 形式 | 用途 |
|---|---|
| `[net/tcp $port $on-connect]` | 启动 TCP 服务器 |
| `[net/udp $port $on-data]` | 启动 UDP 服务器 |
| `[net/connect $host $port]` | 出站 TCP |
| `[net/connect-tls $host $port]` | 出站 TLS |
| `[net/send $conn $data]` | 发送 |
| `[net/on-line $conn $fn]` | 按行回调 |
| `[net/on-data $conn $fn]` | 按读取回调 |
| `[net/on-close $conn $fn]` | 关闭回调 |
| `[net/stop $srv]` / `[net/start $srv]` | 暂停/恢复 |
| `[net/close $srv]` | 永久关闭 |
| `[net/list]` / `[net/info $srv]` | 内省 |