feat(listen): 自 go-hua 迁入监听器封装
- net.Listener 统一封装与 Close 解除 Accept 阻塞, 附测试、例程与 README
This commit is contained in:
@@ -0,0 +1,47 @@
|
|||||||
|
// listen 包示例:TCP 监听封装(NewListen/Accept/Close/Addr)
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"git.zeroonesoft.cn/golib/zogo/listen"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// 创建 TCP 监听(端口 0 表示由系统分配)
|
||||||
|
l, err := listen.NewListen("tcp", "127.0.0.1:0")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
fmt.Println("监听地址:", l.Addr())
|
||||||
|
|
||||||
|
// echo 服务
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
conn, err := l.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go func(c net.Conn) {
|
||||||
|
defer c.Close()
|
||||||
|
scanner := bufio.NewScanner(c)
|
||||||
|
for scanner.Scan() {
|
||||||
|
fmt.Fprintf(c, "echo: %s\n", scanner.Text())
|
||||||
|
}
|
||||||
|
}(conn)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 客户端连接测试
|
||||||
|
conn, err := net.Dial("tcp", l.Addr().String())
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
fmt.Fprintln(conn, "hello go-hua")
|
||||||
|
line, _ := bufio.NewReader(conn).ReadString('\n')
|
||||||
|
fmt.Print("收到:", line)
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# listen
|
||||||
|
|
||||||
|
标准库 `net.Listener` 的简单封装,提供统一的监听器接口与关闭语义。
|
||||||
|
|
||||||
|
> 迁移自 go-hua/listen,代码保持原样。
|
||||||
|
|
||||||
|
## 用法
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "git.zeroonesoft.cn/golib/zogo/listen"
|
||||||
|
|
||||||
|
l, err := listen.NewListen("tcp", ":9000")
|
||||||
|
if err != nil { ... }
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
for {
|
||||||
|
c, err := l.Accept() // Close 后 Accept 立即解除阻塞返回错误
|
||||||
|
if err != nil { break }
|
||||||
|
go handle(c)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
完整可运行例程:[examples/listen/main.go](../examples/listen/main.go)
|
||||||
|
|
||||||
|
## 注意
|
||||||
|
|
||||||
|
- `Close` 后阻塞中的 `Accept` 会立即返回错误,无需额外唤醒逻辑。
|
||||||
|
- 新代码若无需扩展点,直接用标准库 `net.Listen` 亦可。
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
// Package listen 对标准库 net.Listener 的简单封装,提供统一的监听器接口。
|
||||||
|
package listen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Listener 对 net.Listener 的封装
|
||||||
|
type Listener struct {
|
||||||
|
listener net.Listener
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewListen 创建监听器
|
||||||
|
// network 如 "tcp",address 如 "127.0.0.1:8080"
|
||||||
|
func NewListen(network, address string) (*Listener, error) {
|
||||||
|
listener, err := net.Listen(network, address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
l := new(Listener)
|
||||||
|
l.listener = listener
|
||||||
|
return l, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept waits for and returns the next connection to the listener.
|
||||||
|
func (l *Listener) Accept() (net.Conn, error) {
|
||||||
|
conn, err := l.listener.Accept()
|
||||||
|
return conn, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the listener.
|
||||||
|
// Any blocked Accept operations will be unblocked and return errors.
|
||||||
|
func (l *Listener) Close() error {
|
||||||
|
return l.listener.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addr returns the listener's network address.
|
||||||
|
func (l *Listener) Addr() net.Addr {
|
||||||
|
return l.listener.Addr()
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package listen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewListenAndEcho(t *testing.T) {
|
||||||
|
l, err := NewListen("tcp", "127.0.0.1:0")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewListen: %v", err)
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
if l.Addr() == nil {
|
||||||
|
t.Fatal("Addr 不应为 nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
conn, err := l.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go func(c net.Conn) {
|
||||||
|
defer c.Close()
|
||||||
|
scanner := bufio.NewScanner(c)
|
||||||
|
for scanner.Scan() {
|
||||||
|
c.Write(append([]byte("echo:"), scanner.Bytes()...))
|
||||||
|
c.Write([]byte("\n"))
|
||||||
|
}
|
||||||
|
}(conn)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
|
||||||
|
conn, err := net.Dial("tcp", l.Addr().String())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Dial: %v", err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
conn.Write([]byte("hello\n"))
|
||||||
|
conn.SetReadDeadline(time.Now().Add(2 * time.Second))
|
||||||
|
line, err := bufio.NewReader(conn).ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ReadString: %v", err)
|
||||||
|
}
|
||||||
|
if line != "echo:hello\n" {
|
||||||
|
t.Errorf("echo = %q, want echo:hello\\n", line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloseUnblocksAccept(t *testing.T) {
|
||||||
|
l, err := NewListen("tcp", "127.0.0.1:0")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewListen: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
accepted := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
_, err := l.Accept()
|
||||||
|
accepted <- err
|
||||||
|
}()
|
||||||
|
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
if err := l.Close(); err != nil {
|
||||||
|
t.Fatalf("Close: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err := <-accepted:
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Close 后 Accept 应返回错误")
|
||||||
|
}
|
||||||
|
case <-time.After(2 * time.Second):
|
||||||
|
t.Error("Close 未解除 Accept 阻塞")
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user