2026-04-15 15:24:36 +08:00
|
|
|
|
# notify
|
|
|
|
|
|
|
|
|
|
|
|
`b612.me/notify` 是一个面向点对点直连场景的 Go 通信基础包,覆盖消息信令、流式传输、批量数据通道和文件传输内核能力。
|
|
|
|
|
|
|
|
|
|
|
|
## 模块定位
|
|
|
|
|
|
|
|
|
|
|
|
- 消息面:`Send`、`SendWait`、`Reply`、`SetLink`
|
|
|
|
|
|
- 流式数据面:`OpenStream`
|
|
|
|
|
|
- 记录流数据面:`OpenRecordStream`
|
|
|
|
|
|
- 批量数据面:`OpenBulk`(`shared` / `dedicated`)
|
|
|
|
|
|
- 文件传输内核:transfer control / progress / resume
|
2026-04-15 19:52:45 +08:00
|
|
|
|
- 观测面:runtime snapshot / diagnostics summary
|
2026-04-15 15:24:36 +08:00
|
|
|
|
- 会话模型:`LogicalConn`(逻辑会话)与 `TransportConn`(物理承载)分离
|
|
|
|
|
|
|
|
|
|
|
|
## 版本要求
|
|
|
|
|
|
|
|
|
|
|
|
- Go `1.24+`
|
|
|
|
|
|
|
|
|
|
|
|
## 安全初始化要求
|
|
|
|
|
|
|
|
|
|
|
|
`Client` / `Server` 在 `Connect` / `Listen` 前必须完成安全配置。默认使用现代 PSK 方案。
|
|
|
|
|
|
|
|
|
|
|
|
- 客户端:`UseModernPSKClient`
|
|
|
|
|
|
- 服务端:`UseModernPSKServer`
|
|
|
|
|
|
|
|
|
|
|
|
未配置时会返回 `errModernPSKRequired`。
|
|
|
|
|
|
|
2026-04-20 16:35:44 +08:00
|
|
|
|
## 安全模式选择
|
|
|
|
|
|
|
|
|
|
|
|
- `UseModernPSKClient` / `UseModernPSKServer`
|
|
|
|
|
|
- bootstrap 和稳态传输都由 `notify` 自己保护
|
|
|
|
|
|
- 适合默认场景
|
|
|
|
|
|
- 支持 peer attach 显式认证、抗重放,以及在需要时协商前向保密
|
|
|
|
|
|
- `UsePSKOverExternalTransportClient` / `UsePSKOverExternalTransportServer`
|
|
|
|
|
|
- bootstrap 仍用 PSK 做认证
|
|
|
|
|
|
- 稳态阶段信任外部物理通道,不再做 `notify` 内层加密
|
|
|
|
|
|
- 适合 `tls.Conn` 或调用方自认可信的外部通道
|
|
|
|
|
|
- 不支持 `RequireForwardSecrecy`
|
|
|
|
|
|
- `UseNestedSecurityClient` / `UseNestedSecurityServer`
|
|
|
|
|
|
- 外层已有可信通道,但仍保留 `notify` 内层保护
|
|
|
|
|
|
- 适合需要“外层可信 + 内层独立保护”的场景
|
|
|
|
|
|
|
2026-04-15 15:24:36 +08:00
|
|
|
|
## 快速开始
|
|
|
|
|
|
|
|
|
|
|
|
服务端:
|
|
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"log"
|
|
|
|
|
|
|
|
|
|
|
|
"b612.me/notify"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
|
srv := notify.NewServer()
|
|
|
|
|
|
if err := notify.UseModernPSKServer(srv, []byte("shared-secret"), nil); err != nil {
|
|
|
|
|
|
log.Fatal(err)
|
|
|
|
|
|
}
|
|
|
|
|
|
srv.SetLink("ping", func(msg *notify.Message) {
|
|
|
|
|
|
_ = msg.Reply([]byte("pong"))
|
|
|
|
|
|
})
|
|
|
|
|
|
if err := srv.Listen("tcp", "127.0.0.1:28080"); err != nil {
|
|
|
|
|
|
log.Fatal(err)
|
|
|
|
|
|
}
|
|
|
|
|
|
select {}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
客户端:
|
|
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"log"
|
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
|
|
"b612.me/notify"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
|
cli := notify.NewClient()
|
|
|
|
|
|
if err := notify.UseModernPSKClient(cli, []byte("shared-secret"), nil); err != nil {
|
|
|
|
|
|
log.Fatal(err)
|
|
|
|
|
|
}
|
|
|
|
|
|
if err := cli.Connect("tcp", "127.0.0.1:28080"); err != nil {
|
|
|
|
|
|
log.Fatal(err)
|
|
|
|
|
|
}
|
|
|
|
|
|
defer cli.Stop()
|
|
|
|
|
|
|
|
|
|
|
|
reply, err := cli.SendWait("ping", []byte("hello"), 5*time.Second)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
log.Fatal(err)
|
|
|
|
|
|
}
|
|
|
|
|
|
log.Printf("reply=%s", string(reply.Value))
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-20 16:35:44 +08:00
|
|
|
|
## 连接入口与物理连接语义
|
|
|
|
|
|
|
|
|
|
|
|
- `Connect` / `ConnectTimeout`
|
|
|
|
|
|
- 由 `notify` 自己拨号
|
|
|
|
|
|
- 支持重连,也支持 dedicated bulk 额外 sidecar 连接
|
|
|
|
|
|
- `ConnectByFactory`
|
|
|
|
|
|
- 调用方提供 `dialFn`
|
|
|
|
|
|
- `notify` 会在需要时再次调用 `dialFn`,因此仍支持重连和 dedicated bulk
|
|
|
|
|
|
- `ConnectByConn`
|
|
|
|
|
|
- 调用方注入一个已经建立好的 `net.Conn`
|
|
|
|
|
|
- 该模式被视为“单物理连接模式”
|
|
|
|
|
|
- `OpenDedicatedBulk` 会直接返回错误
|
|
|
|
|
|
- `OpenBulk` 使用 `auto` 模式时会自动回退到 `shared`
|
|
|
|
|
|
- `ListenByListener`
|
|
|
|
|
|
- 服务端复用调用方提供的 `net.Listener`
|
|
|
|
|
|
- 适合需要和现有 listener 栈整合的场景
|
|
|
|
|
|
|
|
|
|
|
|
`dedicated bulk` 依赖额外物理连接,因此只适用于可再次拨号的 transport source。
|
|
|
|
|
|
|
|
|
|
|
|
## Peer Attach 安全策略
|
|
|
|
|
|
|
|
|
|
|
|
可通过 `SetPeerAttachSecurityConfig` 配置逻辑会话 attach 阶段的额外保护。
|
|
|
|
|
|
|
|
|
|
|
|
- `RequireExplicitAuth`
|
|
|
|
|
|
- 要求 peer attach 使用显式认证
|
|
|
|
|
|
- `RequireChannelBinding`
|
|
|
|
|
|
- 要求 attach 绑定到底层可信通道
|
|
|
|
|
|
- 启用后会隐式要求显式认证
|
|
|
|
|
|
- `ChannelBinding`
|
|
|
|
|
|
- 由调用方提供 channel binding 提取函数
|
|
|
|
|
|
- 适合外层 TLS 或其他可信通道整合
|
|
|
|
|
|
- `ReplayWindow` / `ReplayCapacity`
|
|
|
|
|
|
- 控制 attach 抗重放窗口和缓存容量
|
|
|
|
|
|
|
|
|
|
|
|
如果你选择 `UsePSKOverExternalTransport*`,并且希望 attach 阶段显式绑定到外层可信信道,建议同时配置 channel binding。
|
|
|
|
|
|
|
2026-04-15 19:52:45 +08:00
|
|
|
|
## RecordStream 说明
|
|
|
|
|
|
|
|
|
|
|
|
`RecordStream` 构建在 `Stream` 之上,适合“有边界的顺序记录”场景。
|
|
|
|
|
|
|
|
|
|
|
|
- 写入入口:`OpenRecordStream`、`WriteRecord`
|
|
|
|
|
|
- 接收入口:`ReadRecord`
|
|
|
|
|
|
- 确认入口:`AckRecord`
|
|
|
|
|
|
- 检查点:`Barrier`、`BarrierTo`
|
|
|
|
|
|
- 错误回包:`RecordFailure`
|
|
|
|
|
|
|
|
|
|
|
|
确认语义:
|
|
|
|
|
|
|
|
|
|
|
|
- `AckRecord` 表示“该序号及其之前的连续记录已完成 apply”,不是“已收到”
|
|
|
|
|
|
- `Barrier` / `BarrierTo` 等待的是对端 `apply-complete` 的最大连续序号
|
|
|
|
|
|
- `RecordFailure` 会返回 `FailedSeq`、`Code`、`Retryable`、`Message`
|
|
|
|
|
|
|
|
|
|
|
|
兼容与传输:
|
|
|
|
|
|
|
|
|
|
|
|
- record stream 在打开阶段协商 batch ack 能力
|
|
|
|
|
|
- 双端都支持时,累计 `AckSeq` 会随 batch header piggyback 发送
|
|
|
|
|
|
- 对端不支持时,自动回退到独立 ack frame
|
|
|
|
|
|
- mixed-version peer 可以互通,不要求双方同时升级
|
|
|
|
|
|
|
|
|
|
|
|
## 诊断快照
|
|
|
|
|
|
|
|
|
|
|
|
顶层诊断入口:
|
|
|
|
|
|
|
|
|
|
|
|
- `GetClientDiagnosticsSnapshot`
|
|
|
|
|
|
- `GetServerDiagnosticsSnapshot`
|
|
|
|
|
|
|
|
|
|
|
|
快照内容:
|
|
|
|
|
|
|
|
|
|
|
|
- 会话运行态:client / server runtime
|
|
|
|
|
|
- 数据面快照:`StreamSnapshot`、`BulkSnapshot`、`RecordSnapshot`
|
|
|
|
|
|
- 文件传输快照:`TransferSnapshot`
|
|
|
|
|
|
- 汇总视图:`DiagnosticsSummary`
|
|
|
|
|
|
|
|
|
|
|
|
`RecordSnapshot` / `DiagnosticsSummary.RecordTelemetry` 当前覆盖:
|
|
|
|
|
|
|
|
|
|
|
|
- batch / ack / error frame 收发计数
|
|
|
|
|
|
- piggyback ack 命中计数
|
|
|
|
|
|
- barrier 等待时间拆分:`flush` / `apply`
|
|
|
|
|
|
- `outstanding records/bytes`
|
|
|
|
|
|
- `pending apply / pending ack / peak pending apply`
|
|
|
|
|
|
|
2026-04-15 15:24:36 +08:00
|
|
|
|
## 传输与 IPC
|
|
|
|
|
|
|
|
|
|
|
|
- `tcp`
|
|
|
|
|
|
- `udp`
|
|
|
|
|
|
- `unix`
|
|
|
|
|
|
- `npipe`(Windows)
|
|
|
|
|
|
|
|
|
|
|
|
示例目录:
|
|
|
|
|
|
|
|
|
|
|
|
- [examples/signal](/mnt/c/coding/gocode/src/b612.me/notify/examples/signal)
|
|
|
|
|
|
|
|
|
|
|
|
## 现代 PSK 与兼容入口
|
|
|
|
|
|
|
|
|
|
|
|
现代方案特性:
|
|
|
|
|
|
|
|
|
|
|
|
- 共享密钥派生(Argon2id)
|
|
|
|
|
|
- 消息层加密(AES-GCM)
|
|
|
|
|
|
- `stream` / `bulk` fast path 复用现代编码栈
|
2026-04-20 16:35:44 +08:00
|
|
|
|
- peer attach 显式认证 / 抗重放
|
|
|
|
|
|
- 可选 channel binding
|
|
|
|
|
|
- 可选前向保密(`UseModernPSK*` / `UseNestedSecurity*`)
|
2026-04-15 15:24:36 +08:00
|
|
|
|
|
|
|
|
|
|
兼容入口仍保留,但属于历史路径:
|
|
|
|
|
|
|
|
|
|
|
|
- `UseLegacySecurityClient`
|
|
|
|
|
|
- `UseLegacySecurityServer`
|
|
|
|
|
|
- `ExchangeKey`
|
|
|
|
|
|
- `SetSecretKey`
|
|
|
|
|
|
- `SetMsgEn` / `SetMsgDe`
|
|
|
|
|
|
|
|
|
|
|
|
## 发布前检查
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
export SENTRUX_SKIP_GRAMMAR_DOWNLOAD='1'
|
|
|
|
|
|
sentrux check .
|
|
|
|
|
|
env GOCACHE=/tmp/b612-gocache GOMODCACHE=/tmp/b612-gomodcache go test ./...
|
|
|
|
|
|
env GOCACHE=/tmp/b612-gocache GOMODCACHE=/tmp/b612-gomodcache go test -race ./...
|
|
|
|
|
|
env GOCACHE=/tmp/b612-gocache GOMODCACHE=/tmp/b612-gomodcache go vet ./...
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
手工 soak 测试(可选):
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
env GOCACHE=/tmp/b612-gocache GOMODCACHE=/tmp/b612-gomodcache \
|
|
|
|
|
|
go test -tags notify_manual_soak -run 'Test_ServerTuAndClientCommon|Test_normal|Test_normal_udp'
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 兼容性说明
|
|
|
|
|
|
|
|
|
|
|
|
- 对外主入口保留:`NewClient`、`NewServer`、`Connect`、`Listen`、`SetLink`、`SetDefaultLink`、`Send`、`SendWait`、`SendObj`、`Reply`、`Stop`
|
|
|
|
|
|
- 内部主对象已迁移为 `LogicalConn` / `TransportConn`
|
|
|
|
|
|
- `ClientConn` 作为兼容适配层继续保留
|