Files
4566704 96c63fb153 feat(spmux): 自 go-hua 迁入单端口多路复用并解耦日志依赖
- go-hua/log 已弃用: *log.Logger 参数改为两方法接口 spmux.Logger
  (*logrus.Logger 天然满足), 传 nil 静默(logErrorf/logDebugf 判空)
- 协议识别改用 zogo/httpprobe; 测试与例程同步适配
- 附测试、examples/spmux 例程(含 Logger 适配演示)与包 README
2026-09-20 12:37:10 +08:00

90 lines
2.1 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// spmux 包示例:单端口多路复用——同一端口上按报文特征把流量分发给 HTTP / HTTPS / TCP 三个 Listener
package main
import (
"bufio"
"fmt"
"io"
"log"
"net"
"net/http"
"os"
"time"
"git.zeroonesoft.cn/golib/zogo/spmux"
)
// stdoutLogger 适配 spmux.Logger 接口(*logrus.Logger 天然满足,无需适配)
type stdoutLogger struct{}
func (stdoutLogger) Debugf(format string, a ...any) { log.Printf("DEBUG "+format, a...) }
func (stdoutLogger) Errorf(format string, a ...any) { log.Printf("ERROR "+format, a...) }
func main() {
// 找一个空闲端口
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
panic(err)
}
port := l.Addr().(*net.TCPAddr).Port
l.Close()
// 创建端口复用器(logger 实现 spmux.Logger 即可,也可传 nil 静默)
pm := spmux.NewPortMux(port, stdoutLogger{})
if err := pm.Start(); err != nil {
panic(err)
}
// HTTP 流量交给 HttpListener
go http.Serve(pm.HttpListener(), http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "hello from http over spmux")
}))
// 其他 TCP 流量交给 TcpListener(echo 服务,回显内容并补换行)
tcpL := pm.TcpListener()
go func() {
for {
c, err := tcpL.Accept()
if err != nil {
return
}
go func(cc net.Conn) {
defer cc.Close()
buf := make([]byte, 1024)
for {
n, err := cc.Read(buf)
if n > 0 {
cc.Write(append(buf[:n:n], '\n'))
}
if err != nil {
return
}
}
}(c)
}
}()
time.Sleep(100 * time.Millisecond)
// HTTP 请求会被识别并路由到 HttpListener
resp, err := http.Get(fmt.Sprintf("http://127.0.0.1:%d/", port))
if err != nil {
panic(err)
}
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
fmt.Print("HTTP 响应:", string(body))
// 普通数据会被路由到 TcpListener
raw, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", port))
if err != nil {
panic(err)
}
fmt.Fprint(raw, "ping")
line, _ := bufio.NewReader(raw).ReadString('\n')
fmt.Print("TCP 响应:", line)
raw.Close()
_ = os.Stdout
}