feat(listen): 自 go-hua 迁入监听器封装
- net.Listener 统一封装与 Close 解除 Accept 阻塞, 附测试、例程与 README
This commit is contained in:
@@ -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