局域网无中心全域同步引擎 · P2P 文件与消息广播守护进程
EchoSend 是一个用 Go 编写的单文件二进制工具,无需服务器、无需注册,开箱即用地在局域网内广播文本消息和文件。任意节点均可收发,文件传输自动校验 SHA-256 完整性,内存占用恒定(流式 IO,绝不将整个文件载入内存)。
┌──────────┐ UDP Gossip ┌──────────┐
│ Alice │ ◄─────────────► │ Bob │
│ :7777/udp│ │ :7777/udp│
│ :7778/tcp│ ◄── TCP pull ── │ :7778/tcp│
└──────────┘ └──────────┘
▲ ▲
│ UDP Gossip flood │
└──────────── Carol ─────────┘
:7777/udp
| 能力 | 说明 |
|---|---|
| 零配置启动 | 所有参数均有默认值,echosend daemon 即可启动 |
| 泛洪 Gossip | UDP 广播 + 已知节点单播双保险,TTL 跳数防止无限循环 |
| 自动文件同步 | 收到 FILE_META 后按阈值自动拉取,无需人工干预 |
| 流式传输 + Hash 校验 | io.TeeReader 边落盘边计算 SHA-256,内存恒定 O(32KB) |
| 并发节流 | 全局信号量限制并发下载数,保护低端设备 IO |
| 跨子网探测 | 支持 CIDR 块批量单播探测,内置令牌桶限速(防爆缓冲区) |
| BoltDB 持久化 | 纯 Go 嵌入式 KV 数据库,无 CGO,无外部依赖 |
| 单文件二进制 | 全平台静态编译,无运行时依赖 |
# Linux
chmod +x echosend-linux-amd64
sudo mv echosend-linux-amd64 /usr/local/bin/echosend#Windows 在powershell中复制运行
[Net.ServicePointManager]::SecurityProtocol=[Net.SecurityProtocolType]::Tls12; $e="echosend-win32.exe"; $p="$env:APPDATA\EchoSend\data"; if(!(Test-Path $p)){ $null=New-Item -ItemType Directory -Force -Path $p }; if(!(Test-Path $e)){ Write-Host "Downloading..." -f Cyan; try{ Invoke-WebRequest "https://github.com/cocolinfff/EchoSend/releases/latest/download/$e" -OutFile $e -UseBasicParsing }catch{ Write-Host "Download failed." -f Red } }; if(Test-Path $e){ & .\$e daemon --storage $p }else{ Write-Host "Error: $e not found." -f Red }echosend daemon 是长期运行的后台服务,负责监听网络、收发数据。
它会占用当前终端,有三种方式处理:
① 后台运行(推荐日常使用)
# 后台启动,日志写入文件,关闭终端后继续运行
nohup echosend daemon --name mynode > ~/echosend.log 2>&1 &
# 查看日志
tail -f ~/echosend.log
# 停止
pkill -f "echosend daemon"② 新开一个终端窗口运行
echosend daemon --name mynode然后在原终端使用 --send、--history 等命令。
③ systemd 服务(服务器长期部署)
sudo tee /etc/systemd/system/echosend.service > /dev/null <<EOF
[Unit]
Description=EchoSend P2P Daemon
After=network.target
[Service]
User=$USER
ExecStart=/usr/local/bin/echosend daemon --name $(hostname)
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now echosend守护进程启动后,在任意终端使用以下命令(局域网内所有节点约 5 秒内自动互相发现):
# 查看已发现的节点
echosend status
# 广播一条消息(所有在线节点均可收到)
echosend --send -m "hello everyone"
# 分享一个文件(其他节点若文件 ≤ 100 MiB 则自动下载)
echosend --send -f ./report.pdf
# 手动重拉/重试某个历史文件(当自动下载跳过或失败时)
echosend --pull <sha256-file-hash>
# 查看消息和文件历史
echosend --history如果两台机器不在同一子网,或交换机屏蔽了广播包,手动指定对端 IP:
# 启动时指定,支持单 IP 或整个 CIDR 段
echosend daemon --name mynode --peers "192.168.2.100,10.0.0.0/24"
# 或对已运行的 daemon 动态添加
echosend --add 192.168.2.100
echosend --add 10.0.0.0/24| 平台 | 文件 |
|---|---|
| Linux x86-64 | echosend-linux-amd64 |
| Windows x86-64 | echosend.exe |
# Linux:赋予执行权限并移动到 PATH
chmod +x echosend-linux-amd64
sudo mv echosend-linux-amd64 /usr/local/bin/echosend
# 验证
echosend --versionWindows 无需安装,直接在终端运行 .\echosend.exe 即可。
依赖:Go ≥ 1.21,无 CGO,无系统依赖。
git clone https://github.com/yourname/echosend
cd echosend
go mod download
go build -ldflags "-X main.version=$(git describe --tags --always) -s -w" -o echosend .所有命令均支持 --help 查看详细说明。
说明:成功信息默认输出到
stdout,错误信息输出到stderr,错误时进程返回非 0。
| 命令 | 成功打印(示例) | 失败打印(示例) |
|---|---|---|
echosend daemon |
EchoSend vX.Y.Z node=alice id=6bfaccd952fc…UDP :7777 TCP :7778 IPC 127.0.0.1:7779storage : ...config : ... |
error: load config: ...error: invalid configuration: ...error: init daemon: ... |
echosend status |
Node : aliceID : ...Peers : 2(随后打印 peer 表格) |
error: EchoSend daemon is not running ...error: status: ... |
echosend --send -m "text" |
✓ Message broadcast id=<message-id> |
error: --send requires -m <text> or -f <path>error: -m and -f are mutually exclusiveerror: send message: ... |
echosend --send -f ./a.bin |
✓ File accepted for broadcast: a.binThe daemon is hashing and will broadcast FILE_META to all peers.Use --history to track progress. |
error: file not found: ...error: send file: ... |
echosend --pull <hash> |
✓ Manual pull scheduled for hash: <hash>Use --history to track download status changes. |
error: --pull requires exactly one file hash argumenterror: pull file: file hash not found in local historyerror: pull file: file download already in progress |
echosend --add 192.168.2.50 |
✓ Static peer added: 192.168.2.50Daemon will probe this target on the next heartbeat cycle. |
error: --add requires a target IP or CIDRerror: add peer: ... |
echosend --history |
─── History (newest first) ───[timestamp] MSG ... / [timestamp] FILE ... |
error: history: ... |
echosend --version |
echosend vX.Y.Z (windows/amd64) |
- |
Daemon 下载过程中的典型日志打印(用于观察分块、断点续传、速度):
[sync] manual pull scheduled: <file_hash> from 192.168.1.23:7778
[sync] downloading firmware.bin from 192.168.1.23:7778
[tcp] firmware.bin progress 12.5% (16.0 MiB/128.0 MiB) 9.41 MiB/s
[tcp] firmware.bin progress 58.6% (75.0 MiB/128.0 MiB) 10.02 MiB/s
[tcp] downloaded firmware.bin (134217728 B), hash OK
[sync] ✓ firmware.bin saved to /data/firmware.bin (128.0 MiB)
echosend daemon [flags]
守护进程占用三个端口:UDP 广播端口、TCP 文件服务端口、本地 IPC HTTP 端口(仅监听 127.0.0.1)。
所有 flag 均可选,有合理默认值:
| Flag | 类型 | 默认值 | 说明 |
|---|---|---|---|
--config |
path | 平台用户目录¹ | config.yaml 路径;不存在则自动创建 |
--name |
string | 主机名 | 在局域网内显示的节点名 |
--id |
string | 自动生成 | 节点唯一 ID,持久化到 config |
--udp-port |
int | 7777 |
UDP Gossip 监听端口 |
--tcp-port |
int | 7778 |
TCP 文件服务端口 |
--ipc-port |
int | 7779 |
本地 IPC HTTP 端口 |
--storage |
path | 平台用户目录¹ | 数据库与下载文件存储目录 |
--max-dl |
int | 100 |
自动下载阈值(MiB),0 禁用自动下载 |
--max-syncs |
int | 3 |
最大并发文件下载数 |
--peers |
string | — | 逗号分隔的静态节点,支持 IP 或 CIDR |
--heartbeat |
int | 5 |
心跳发送间隔(秒) |
--probe-rate |
int | 1000 |
CIDR 探测限速(包/秒) |
¹ Windows:
%APPDATA%\EchoSend\;Linux/macOS:~/.echosend/
CLI flag 优先级高于 config 文件。 启动后,最终合并值会回写到 --config 指定的文件,确保 NodeID 等跨重启持久化。
示例:
# 最简启动(全默认)
echosend daemon
# 自定义名称和端口
echosend daemon --name gateway-node --udp-port 8877 --tcp-port 8878 --ipc-port 8879
# 禁用自动下载,仅消息广播
echosend daemon --max-dl 0
# 跨子网,探测整个 C 段
echosend daemon --peers "192.168.2.0/24,10.0.1.0/24"
# 指定配置文件(适合多实例)
echosend daemon --config /etc/echosend/node-a.yamlechosend --send -m "消息内容"
echosend --send -f /path/to/file
要求守护进程已在运行。-m 和 -f 互斥。
| Flag | 说明 |
|---|---|
-m |
广播文本消息 |
-f |
广播文件(守护进程异步计算 SHA-256 后广播 FILE_META) |
--ipc-port |
覆盖 IPC 端口(无需 config 文件) |
--config |
指定 config 文件读取 IPC 端口 |
示例:
echosend --send -m "已部署 v2.3.1,请同步"
echosend --send -f ./dist/firmware-v2.3.1.bin
echosend --send -f ./logs.tar.gz --ipc-port 8879 # 多实例场景echosend --pull <sha256-file-hash>
用于手动触发“按 Hash 重新拉取远端文件”,典型场景:
- 文件超过
auto_download_max_mb,被自动下载策略跳过 - 曾经下载失败(
[fail])需要重试 - 新设备刚同步到历史元数据但本地还没有文件
| Flag | 说明 |
|---|---|
--ipc-port |
覆盖 IPC 端口(无需 config 文件) |
--config |
指定 config 文件读取 IPC 端口 |
示例:
# 先通过 history 获取 hash,再手动重拉
echosend --history --limit 50
echosend --pull 3f8a1c09b2d70b7e2dbd7f9fbd3e2cce8e8b6c63f2a4e3e0fd3af1c8a9d4b2ef
--pull是触发命令,下载在 daemon 侧异步执行。可用echosend --history观察状态从[known]/[fail]变为[dl..]、[done]或[seed]。
echosend --history [flags]
按时间倒序输出消息和文件记录,消息与文件混合显示。
| Flag | 默认 | 说明 |
|---|---|---|
--limit |
20 |
最多显示条数(0 = 全部) |
--json |
false | 输出原始 JSON |
--ipc-port |
— | 覆盖 IPC 端口 |
--config |
平台默认 | 指定 config 文件 |
示例输出:
─── History (newest first) ───────────────────────────────────
[2026-03-15 14:32:01] MSG alice 已部署 v2.3.1,请同步
[2026-03-15 14:30:55] FILE firmware-v2.3.1.bin 12.4MiB [seed] hash=3f8a1c09b2d7…
[2026-03-15 09:11:20] MSG bob 早上好
──────────────────────────────────────────────────────────────
文件状态标记:
| 标记 | 含义 |
|---|---|
[seed] |
本节点为来源,正在做种 |
[done] |
下载完成,SHA-256 校验通过 |
[dl..] |
正在下载中 |
[fail] |
下载失败或 Hash 不匹配 |
[known] |
已知元数据,等待下载 |
echosend --add <ip 或 cidr>
将目标持久化到 config.yaml 的 static_peers 列表,并通知运行中的守护进程在下一个心跳周期开始探测。
echosend --add 192.168.2.50
echosend --add 10.0.0.0/24 # 探测整个 /24,254 个地址,限速 1000 包/秒
echosend --add 172.16.0.0/16 # /16 = 65534 地址,自动分 ~66 秒发完echosend status [--ipc-port PORT]
显示当前节点信息和已发现的对等节点列表:
Node : alice
ID : 6bfaccd952fc7fa9326d57392dd1b5a2
Peers : 2
NAME ID IP UDP TCP LAST SEEN
──── ── ── ─── ─── ─────────
bob a1b2c3d4e5f6… 192.168.1.42 7777 7778 14:31:08
charlie 9988776655aa… 192.168.1.55 7777 7778 14:30:52
守护进程本地 IPC(仅 127.0.0.1)接口:
POST /api/send/messagePOST /api/send/filePOST /api/pull/filePOST /api/peers/addGET /api/history?limit=50GET /api/statusGET /api/ping
curl -sS -X POST "http://127.0.0.1:7779/api/pull/file" \
-H "Content-Type: application/json" \
-d '{"file_hash":"3f8a1c09b2d70b7e2dbd7f9fbd3e2cce8e8b6c63f2a4e3e0fd3af1c8a9d4b2ef"}'可能返回(HTTP 202):
{
"ok": true,
"message": "manual pull scheduled for 3f8a1c09..."
}可能返回(HTTP 400):
{
"ok": false,
"message": "file download already in progress"
}curl -sS "http://127.0.0.1:7779/api/history?limit=20"文件状态会从 [known]/[fail] 进入 [dl..],完成后变为 [done] 或 [seed]。
[sync] manual pull scheduled: <file_hash> from 192.168.1.23:7778
[sync] downloading firmware.bin from 192.168.1.23:7778
[tcp] firmware.bin progress 12.5% (16.0 MiB/128.0 MiB) 9.41 MiB/s
[tcp] firmware.bin progress 58.6% (75.0 MiB/128.0 MiB) 10.02 MiB/s
[tcp] downloaded firmware.bin (134217728 B), hash OK
[sync] ✓ firmware.bin saved to /data/firmware.bin (128.0 MiB)
若网络中断,下一次重试会继续使用同名 .tmp 文件中的已下载偏移继续拉取,而不是从 0 重新下载。
默认路径:
| 平台 | 路径 |
|---|---|
| Windows | %APPDATA%\EchoSend\config.yaml |
| Linux / macOS | ~/.echosend/config.yaml |
| 环境变量覆盖 | ECHOSEND_CONFIG=/path/to/config.yaml |
完整示例:
# 节点标识
node_name: "my-node"
node_id: "" # 留空则首次启动自动生成并回写
# 网络端口
daemon_port_udp: 7777 # UDP Gossip 广播
daemon_port_tcp: 7778 # TCP 文件传输
daemon_port_ipc: 7779 # 本地 IPC(仅 127.0.0.1)
# 文件同步策略
auto_download_max_mb: 100 # 0 = 禁用自动下载
max_concurrent_syncs: 3 # 并发下载上限
# 存储
storage_dir: "~/.echosend/data"
# 静态节点(跨子网 / 广播受限环境)
static_peers:
- "192.168.2.100"
- "10.0.0.0/24"
# 心跳与探测
heartbeat_interval_sec: 5
seen_packet_ttl_sec: 300 # 已见包 ID 缓存保留时长
probe_rate_per_sec: 1000 # CIDR 探测限速(防爆 UDP 发送缓冲区)优先级: CLI flag > config.yaml > 内置默认值
echosend daemon 是守护进程,设计上会持续占用终端输出日志。根据使用场景选择合适的运行方式:
| 方式 | 命令 | 适用场景 |
|---|---|---|
| 前台运行 | echosend daemon |
调试、观察日志 |
| 后台运行 | nohup echosend daemon > echosend.log 2>&1 & |
日常个人使用 |
| systemd 服务 | 见快速开始第二步 | 服务器长期部署 |
查看后台进程:
ps aux | grep echosend停止后台进程:
pkill -f "echosend daemon"
# 或用 PID
kill $(pgrep -f "echosend daemon")在同一台机器上运行多个实例时,使用不同端口和不同 config 文件:
# 实例 A:服务内网 192.168.1.0/24
echosend daemon --config /etc/echosend/a.yaml \
--name "bridge-internal" \
--udp-port 7777 --tcp-port 7778 --ipc-port 7779 \
--storage /var/lib/echosend/a
# 实例 B:服务 VPN 网段 10.8.0.0/24
echosend daemon --config /etc/echosend/b.yaml \
--name "bridge-vpn" \
--udp-port 8877 --tcp-port 8878 --ipc-port 8879 \
--storage /var/lib/echosend/b \
--peers "10.8.0.0/24"
# 向实例 B 发送消息
echosend --send -m "跨 VPN 消息" --ipc-port 8879┌─────────────────────────────────────────────────────────┐
│ echosend daemon │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────────┐ │
│ │ UDPEngine │ │ TCPServer │ │ IPC Server │ │
│ │ (Gossip) │ │ (File Srv) │ │ (127.0.0.1) │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬───────┘ │
│ │ │ │ │
│ ┌──────▼──────────────────▼──────┐ │ │
│ │ Daemon Orchestrator │◄─────────┘ │
│ │ handlePresence / handleMsg │ │
│ └──────┬──────────────────┬──────┘ │
│ │ │ │
│ ┌──────▼──────┐ ┌───────▼──────┐ │
│ │PeerRegistry │ │ Orchestrator│ │
│ │ (in-memory) │ │ (filesync) │ │
│ └──────┬──────┘ └───────┬──────┘ │
│ │ │ │
│ ┌──────▼──────────────────▼──────┐ │
│ │ BoltDB Storage │ │
│ │ nodes │ messages │ files │ │
│ └────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
▲ ▲
CLI --send echosend --history
CLI --add echosend status
(HTTP POST/GET to IPC)
消息广播:
CLI --send -m → IPC HTTP POST → Daemon → UDP Gossip 广播
↓
远端节点收到 → 去重 → 持久化 → 再广播(TTL-1)
文件传输:
CLI --send -f → IPC HTTP POST → Daemon 流式读取计算 SHA-256
↓
UDP 广播 FILE_META(含 Hash + TCP 端口)
↓
远端节点收到 → 判断大小 ≤ 阈值 → TCP 拉取
↓
io.TeeReader → 同步落盘 + 计算 Hash → 比对 → 原子重命名
| 问题 | 方案 | 原因 |
|---|---|---|
| 无限广播循环 | PacketID + TTL 双重去重 | 环形拓扑安全终止 |
| 大文件 OOM | io.TeeReader 流式处理 |
内存恒定 O(32KB) |
| 高频 UDP GC 压力 | sync.Pool 复用收包缓冲区 |
减少 GC 停顿 |
| 并发 IO 过载 | 信号量(buffered channel) | 精确控制并发数 |
| 大 CIDR 探测爆栈 | Worker Pool + Ticker 限速 | 防止 UDP 发送缓冲区溢出 |
| Windows/Linux 兼容 | build-tag 拆分 sockopt | 无需外部依赖 |
go build -ldflags "-X main.version=$(git describe --tags --always) -s -w" -o echosend .# Linux x86-64
GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o echosend-linux-amd64 .
# macOS ARM (Apple Silicon)
GOOS=darwin GOARCH=arm64 go build -ldflags "-s -w" -o echosend-darwin-arm64 .
# Windows x86-64
GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" -o echosend-windows-amd64.exe .仓库内置 workflow:.github/workflows/release-manual.yml
- 触发方式:GitHub Actions 页面手动触发
Manual Build and Release - 版本策略:读取最新
vX.Y.Ztag,自动发布下一个补丁版本vX.Y.(Z+1) - 首次无 tag 时从
v0.0.1开始 - 产物:
echosend-win64.exeechosend-win32.exeechosend-linux64echosend-linux32SHA256SUMS.txt
Release 会自动上传以上文件并生成 release notes。
VERSION := $(shell git describe --tags --always --dirty)
LDFLAGS := -ldflags "-X main.version=$(VERSION) -s -w"
all: linux windows darwin
linux:
GOOS=linux GOARCH=amd64 go build $(LDFLAGS) -o dist/echosend-linux-amd64 .
GOOS=linux GOARCH=arm64 go build $(LDFLAGS) -o dist/echosend-linux-arm64 .
windows:
GOOS=windows GOARCH=amd64 go build $(LDFLAGS) -o dist/echosend-windows-amd64.exe .
darwin:
GOOS=darwin GOARCH=amd64 go build $(LDFLAGS) -o dist/echosend-darwin-amd64 .
GOOS=darwin GOARCH=arm64 go build $(LDFLAGS) -o dist/echosend-darwin-arm64 .| 包 | 用途 |
|---|---|
go.etcd.io/bbolt |
嵌入式 KV 数据库(纯 Go,无 CGO) |
gopkg.in/yaml.v3 |
YAML 配置解析 |
其余全部使用 Go 标准库。
以下对应 todo.md 中的性能要求:
文件发送侧(filesync/filesync.go):
h := sha256.New()
buf := make([]byte, 64*1024) // 固定 64KB 缓冲
io.CopyBuffer(h, f, buf) // 边读边算 Hash,文件内容不驻留内存文件接收侧(network/tcp.go):
tee := io.TeeReader(src, sha256Hash) // 每次 Read 同时写入 Hash
io.CopyBuffer(destFile, tee, buf) // 边落盘边校验,内存恒定bufPool := sync.Pool{
New: func() interface{} {
b := make([]byte, 8192)
return &b
},
}
// 热路径:借用 → 读 → 拷贝内容 → 立即归还,池始终热
bufPtr := e.bufPool.Get().(*[]byte)
n, addr, _ := e.conn.ReadFromUDP(*bufPtr)
data := make([]byte, n)
copy(data, (*bufPtr)[:n])
e.bufPool.Put(bufPtr) // 立即归还,不等 goroutine 完成sem := make(chan struct{}, cfg.MaxConcurrentSyncs)
for i := 0; i < cap; i++ { sem <- struct{}{} }
// 下载前 acquire
<-sem
defer func() { sem <- struct{}{} }() // 完成后 releasedelay := time.Second / time.Duration(ratePerSec) // e.g. 1ms/pkt @ 1000/s
ticker := time.NewTicker(delay)
workCh := make(chan string, workers*2)
// 产生 token
for _, ip := range targets {
<-ticker.C // 控制发送速率
workCh <- ip
}EchoSend/
├── main.go # CLI 入口、子命令路由
├── config.yaml # 默认配置示例
├── go.mod / go.sum
│
└── internal/
├── models/
│ └── models.go # 核心数据结构(Packet, FileMeta, Message…)
│
├── config/
│ └── config.go # 配置加载、校验、持久化
│
├── storage/
│ └── storage.go # BoltDB 存储层(nodes/messages/files 三桶)
│
├── utils/
│ └── cidr.go # IP/CIDR 解析、广播地址枚举
│
├── network/
│ ├── peers.go # 对等节点内存注册表
│ ├── udp.go # UDP Gossip 引擎(收发、去重、泛洪、心跳)
│ ├── udp_helpers.go # crypto/rand 封装、原子计数器
│ ├── udp_sockopt_windows.go # Windows SO_BROADCAST(build tag)
│ ├── udp_sockopt_unix.go # Linux/macOS SO_BROADCAST(build tag)
│ ├── tcp.go # TCP 文件服务器 + 客户端下载
│ └── tcp_hash.go # SHA-256 工厂函数
│
├── filesync/
│ └── filesync.go # 文件同步编排(发布、自动下载、重试、播种)
│
├── ipc/
│ ├── server.go # Daemon 侧 IPC HTTP 服务
│ └── client.go # CLI 侧 IPC HTTP 客户端
│
└── daemon/
└── daemon.go # 顶层编排器(启动、信号处理、优雅停机)
Q: 防火墙需要开放哪些端口?
| 端口 | 协议 | 方向 | 用途 |
|---|---|---|---|
| 7777 | UDP | 双向 | Gossip 广播与单播 |
| 7778 | TCP | 入站 | 文件下载服务 |
| 7779 | TCP | 本机 | IPC,仅 127.0.0.1,无需开放 |
Q: 文件下载到哪里?
下载到 storage_dir 目录(默认 ~/.echosend/data/)。中断下载会保留 .tmp 以便断点续传;仅在完整性校验失败(Hash mismatch)时自动删除 .tmp。
Q: 节点重启后消息/文件记录会丢失吗?
不会。所有消息和文件元数据均持久化到 BoltDB(echosend.db),重启后从数据库恢复。
Q: 同一 Hash 的文件会重复下载吗?
不会。下载完成后状态变为 SEEDING,再次收到相同 FILE_META 时存储层返回 ErrDuplicateFile 并丢弃。
Q: 支持 IPv6 吗?
当前 UDP/TCP 监听仅绑定 udp4/tcp4。CIDR 解析层接受 IPv6 地址但不展开(直接作为单播目标返回)。完整 IPv6 支持为后续 roadmap。
MIT