"完善README文档和代码注释,详细说明框架功能和使用方法"

This commit is contained in:
kingecg 2025-07-01 20:59:02 +08:00
parent 377e51bb40
commit b8c32f11ef
3 changed files with 365 additions and 67 deletions

236
README.md
View File

@ -1,2 +1,236 @@
# network # 高性能网络通信框架
一个用 Go 语言编写的高性能、高并发网络通信框架,支持 TCP 和 UDP 协议。该框架专为高吞吐量和低延迟的网络应用设计,适用于游戏服务器、聊天系统、实时数据处理等场景。
## 主要特性
- **协议支持**:同时支持 TCP 和 UDP 协议
- **高并发处理**:使用工作协程池高效处理请求
- **连接分片管理**:通过分片减少锁竞争,提高并发性能
- **自动连接生命周期管理**:自动处理连接的创建、维护和关闭
- **内存优化**:使用缓冲区池和对象池减少内存分配和 GC 压力
- **优雅关闭**:支持服务器的平滑关闭,确保数据不丢失
- **超时控制**:支持读写超时和空闲连接超时设置
- **可定制的数据包处理**:灵活的数据包接口,支持自定义协议
## 安装
```bash
go get github.com/yourusername/network
```
## 快速开始
### TCP 服务器示例
```go
package main
import (
"log"
"time"
"github.com/yourusername/network"
)
// 自定义数据包
type MyPacket struct {
Type byte
Length uint16
Payload []byte
}
// 实现 Packet 接口
func (p *MyPacket) Decode(data []byte) error {
if len(data) < 3 {
return errors.New("invalid packet")
}
p.Type = data[0]
p.Length = uint16(data[1])<<8 | uint16(data[2])
if len(data) < int(3+p.Length) {
return errors.New("incomplete packet")
}
p.Payload = data[3:3+p.Length]
return nil
}
func (p *MyPacket) Encode() ([]byte, error) {
buf := make([]byte, 3+len(p.Payload))
buf[0] = p.Type
buf[1] = byte(len(p.Payload) >> 8)
buf[2] = byte(len(p.Payload))
copy(buf[3:], p.Payload)
return buf, nil
}
func main() {
// 创建服务器配置
config := network.ServerConfig{
Network: "tcp",
Address: ":8080",
WorkerNum: 100, // 工作协程数量
QueueSize: 1000, // 任务队列大小
MaxConn: 10000, // 最大连接数
ReadTimeout: 30 * time.Second,
IdleTimeout: 5 * time.Minute,
}
// 创建处理函数
handler := func(conn network.Conn, packet network.Packet) (network.Packet, error) {
p := packet.(*MyPacket)
log.Printf("Received packet: Type=%d, Payload=%s", p.Type, string(p.Payload))
// 创建响应
response := &MyPacket{
Type: p.Type,
Payload: []byte("Response: " + string(p.Payload)),
}
return response, nil
}
// 创建数据包工厂函数
packetFactory := func() network.Packet {
return &MyPacket{}
}
// 创建并启动服务器
server := network.NewServer(config, handler, packetFactory)
if err := server.Start(); err != nil {
log.Fatalf("Failed to start server: %v", err)
}
// 等待信号退出
// ...
// 优雅关闭
server.Stop()
}
```
### UDP 服务器示例
```go
package main
import (
"log"
"time"
"github.com/yourusername/network"
)
func main() {
// 创建服务器配置
config := network.ServerConfig{
Network: "udp",
Address: ":8081",
WorkerNum: 50,
QueueSize: 500,
ReadTimeout: 5 * time.Second,
}
// 使用与 TCP 示例相同的 MyPacket 和 handler
// 创建并启动服务器
server := network.NewServer(config, handler, packetFactory)
if err := server.Start(); err != nil {
log.Fatalf("Failed to start UDP server: %v", err)
}
// ...
}
```
## API 文档
### 核心接口
#### `Packet` 接口
```go
type Packet interface {
Decode(data []byte) error
Encode() ([]byte, error)
}
```
所有数据包必须实现此接口,用于数据的序列化和反序列化。
#### `Conn` 接口
```go
type Conn interface {
Read(b []byte) (n int, err error)
Write(b []byte) (n int, err error)
Close() error
RemoteAddr() net.Addr
}
```
表示一个网络连接TCP 和 UDP 连接都实现了此接口。
#### `Handler` 函数类型
```go
type Handler func(conn Conn, packet Packet) (Packet, error)
```
用户定义的处理函数,处理接收到的数据包并返回响应。
### 主要结构体
#### `ServerConfig` 结构体
```go
type ServerConfig struct {
Network string // 网络类型: "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6"
Address string // 监听地址,如 ":8080"
WorkerNum int // 工作协程数量
QueueSize int // 任务队列大小
MaxConn int // 最大连接数
ReadTimeout time.Duration // 读取超时
WriteTimeout time.Duration // 写入超时
IdleTimeout time.Duration // 空闲连接超时
}
```
#### `HighConcurrentServer` 结构体
主要的服务器实现,提供以下方法:
- `NewServer(config ServerConfig, handler Handler, packetType func() Packet) *HighConcurrentServer`:创建新服务器
- `Start() error`:启动服务器
- `Stop()`:停止服务器
- `GetActiveConnections() int64`:获取当前活动连接数
#### `WorkerPool` 结构体
工作协程池,用于高效处理任务:
- `NewWorkerPool(size int, queueSize int) *WorkerPool`:创建工作池
- `Submit(task func())`:提交任务
- `Stop()`:停止工作池
## 性能优化
该框架采用了多种技术来优化性能:
1. **连接分片**:使用多个分片管理连接,每个分片有自己的锁,减少锁竞争
2. **工作协程池**:重用协程处理请求,避免频繁创建和销毁协程的开销
3. **缓冲区池**:使用 `sync.Pool` 复用缓冲区,减少内存分配
4. **非阻塞 I/O**:使用 channel 进行非阻塞的读写操作
5. **批量处理**:在可能的情况下批量处理数据包,减少系统调用
## 贡献指南
欢迎贡献代码、报告问题或提出改进建议。请遵循以下步骤:
1. Fork 仓库
2. 创建功能分支 (`git checkout -b feature/amazing-feature`)
3. 提交更改 (`git commit -m 'Add some amazing feature'`)
4. 推送到分支 (`git push origin feature/amazing-feature`)
5. 创建 Pull Request
## 许可证
[MIT](LICENSE)

View File

@ -1,3 +1,6 @@
// Package network 提供了一个高性能的网络通信框架支持TCP和UDP协议。
// 该框架采用工作池和连接分片管理来实现高并发,支持自定义数据包格式和处理函数。
// 主要特点包括:高并发连接处理、连接生命周期管理、优雅关闭机制和缓冲区复用。
package network package network
import ( import (
@ -14,51 +17,63 @@ import (
// ========== 框架核心接口 ========== // ========== 框架核心接口 ==========
// Packet 用户自定义数据包必须实现的接口 // Packet 用户自定义数据包必须实现的接口
// 用户需要实现这个接口来定义自己的数据包格式,包括编码和解码方法
type Packet interface { type Packet interface {
// 编码为字节流 // Encode 将数据包编码为字节流
// 返回编码后的字节切片和可能的错误
Encode() ([]byte, error) Encode() ([]byte, error)
// 从字节流解码
// Decode 从字节流解码为数据包
// 参数data包含要解码的字节数据
// 返回可能的解码错误
Decode([]byte) error Decode([]byte) error
} }
// Handler 用户自定义的数据处理函数类型 // Handler 用户自定义的数据处理函数类型
// 当收到数据包时,框架会调用这个函数来处理数据
// 参数conn是产生数据的网络连接p是解码后的数据包
// 返回响应数据包和可能的错误
type Handler func(conn net.Conn, p Packet) (Packet, error) type Handler func(conn net.Conn, p Packet) (Packet, error)
// ========== 框架核心结构 ========== // ========== 框架核心结构 ==========
// ServerConfig 服务器配置 // ServerConfig 服务器配置
// 包含服务器运行所需的各种参数设置
type ServerConfig struct { type ServerConfig struct {
Network string // tcp, tcp4, tcp6, udp, udp4, udp6 Network string // 网络类型: tcp, tcp4, tcp6, udp, udp4, udp6
Address string // 监听地址 Address string // 监听地址,格式为 "ip:port",如 ":8080"
MaxConn int // 最大连接数 (TCP only) MaxConn int // 最大连接数 (仅TCP有效)
WorkerNum int // 工作协程数量 WorkerNum int // 工作协程数量默认为CPU核心数的2倍
QueueSize int // 任务队列大小 QueueSize int // 任务队列大小默认为1024
ReadTimeout time.Duration // 读取超时 ReadTimeout time.Duration // 读取超时时间0表示不设置超时
IdleTimeout time.Duration // 空闲超时 IdleTimeout time.Duration // 空闲连接超时时间0表示不清理空闲连接
} }
// connectionShard 连接分片 // connectionShard 连接分片
// 用于高效管理大量连接,减少锁竞争
type connectionShard struct { type connectionShard struct {
conns sync.Map // map[net.Conn]*Connection conns sync.Map // 存储连接的并发安全map键为net.Conn值为*Connection
lock sync.RWMutex lock sync.RWMutex // 读写锁,保护分片操作
lastUsed time.Time lastUsed time.Time // 最后使用时间,用于分片管理
} }
// Connection 连接封装 // Connection 连接封装
// 封装了底层网络连接,提供了缓冲区和通道管理
type Connection struct { type Connection struct {
net.Conn net.Conn // 嵌入底层网络连接
readBuffer *bytes.Buffer readBuffer *bytes.Buffer // 读缓冲区
writeBuffer *bytes.Buffer writeBuffer *bytes.Buffer // 写缓冲区
writeChan chan []byte writeChan chan []byte // 写入通道,用于异步写入
closeChan chan struct{} closeChan chan struct{} // 关闭通道,用于通知协程退出
server *HighConcurrentServer server *HighConcurrentServer // 所属服务器
lastActive time.Time lastActive time.Time // 最后活动时间,用于空闲检测
} }
// WorkerPool 工作协程池 // WorkerPool 工作协程池
// 用于高效处理网络请求,避免为每个连接创建协程
type WorkerPool struct { type WorkerPool struct {
taskQueue chan func() taskQueue chan func() // 任务队列,存储待执行的函数
size int size int // 工作协程数量
} }
// ========== 框架实现 ========== // ========== 框架实现 ==========

139
server.go
View File

@ -1,3 +1,4 @@
// Package network 提供了一个高性能的网络通信框架支持TCP和UDP协议。
package network package network
import ( import (
@ -11,34 +12,45 @@ import (
) )
// HighConcurrentServer 高并发服务器 // HighConcurrentServer 高并发服务器
// 实现了一个高性能、高并发的网络服务器支持TCP和UDP协议
// 主要特点包括:
// - 连接分片管理,减少锁竞争
// - 工作协程池,高效处理请求
// - 自动管理连接生命周期
// - 优雅关闭机制
type HighConcurrentServer struct { type HighConcurrentServer struct {
config ServerConfig config ServerConfig // 服务器配置
listener net.Listener listener net.Listener // TCP监听器
packetConn net.PacketConn packetConn net.PacketConn // UDP包连接
workerPool *WorkerPool workerPool *WorkerPool // 工作协程池
shards []*connectionShard shards []*connectionShard // 连接分片数组
shardCount int shardCount int // 分片数量
handler Handler handler Handler // 用户自定义处理函数
packetType func() Packet // 用于创建新Packet实例的函数 packetType func() Packet // 用于创建新Packet实例的工厂函数
activeConns int64 activeConns int64 // 当前活动连接数
shutdown chan struct{} shutdown chan struct{} // 关闭信号通道
wg sync.WaitGroup wg sync.WaitGroup // 等待组,用于优雅关闭
} }
// NewServer 创建新的高并发服务器 // NewServer 创建新的高并发服务器
// config: 服务器配置
// handler: 用户自定义的数据包处理函数
// packetType: 用于创建新的数据包实例的工厂函数
// 返回初始化好的服务器实例,但尚未启动
func NewServer(config ServerConfig, handler Handler, packetType func() Packet) *HighConcurrentServer { func NewServer(config ServerConfig, handler Handler, packetType func() Packet) *HighConcurrentServer {
// 设置默认值 // 设置默认值
if config.WorkerNum <= 0 { if config.WorkerNum <= 0 {
config.WorkerNum = runtime.NumCPU() * 2 config.WorkerNum = runtime.NumCPU() * 2 // 默认为CPU核心数的2倍
} }
if config.QueueSize <= 0 { if config.QueueSize <= 0 {
config.QueueSize = 1024 config.QueueSize = 1024 // 默认队列大小为1024
} }
if config.MaxConn <= 0 { if config.MaxConn <= 0 {
config.MaxConn = 100000 config.MaxConn = 100000 // 默认最大连接数为10万
} }
// 计算合适的分片数量 // 计算合适的分片数量至少为32且不小于CPU核心数
// 分片数量为2的幂便于哈希分配
shardCount := 32 shardCount := 32
for shardCount < runtime.NumCPU() { for shardCount < runtime.NumCPU() {
shardCount *= 2 shardCount *= 2
@ -53,41 +65,47 @@ func NewServer(config ServerConfig, handler Handler, packetType func() Packet) *
shards: make([]*connectionShard, shardCount), shards: make([]*connectionShard, shardCount),
} }
// 初始化分片 // 初始化连接分片
for i := 0; i < shardCount; i++ { for i := 0; i < shardCount; i++ {
server.shards[i] = &connectionShard{} server.shards[i] = &connectionShard{}
} }
// 初始化工作 // 初始化工作协程
server.workerPool = NewWorkerPool(config.WorkerNum, config.QueueSize) server.workerPool = NewWorkerPool(config.WorkerNum, config.QueueSize)
return server return server
} }
// Start 启动服务器 // Start 启动服务器
// 根据配置的网络类型启动相应的服务器TCP或UDP
// 返回可能的启动错误
func (s *HighConcurrentServer) Start() error { func (s *HighConcurrentServer) Start() error {
switch s.config.Network { switch s.config.Network {
case "tcp", "tcp4", "tcp6": case "tcp", "tcp4", "tcp6":
return s.startTCP() return s.startTCP() // 启动TCP服务器
case "udp", "udp4", "udp6": case "udp", "udp4", "udp6":
return s.startUDP() return s.startUDP() // 启动UDP服务器
default: default:
return fmt.Errorf("unsupported network type: %s", s.config.Network) return fmt.Errorf("unsupported network type: %s", s.config.Network)
} }
} }
// startTCP 启动TCP服务器 // startTCP 启动TCP服务器
// 创建监听器并启动接受连接循环和连接管理协程
// 返回可能的启动错误
func (s *HighConcurrentServer) startTCP() error { func (s *HighConcurrentServer) startTCP() error {
// 创建TCP监听器
ln, err := net.Listen(s.config.Network, s.config.Address) ln, err := net.Listen(s.config.Network, s.config.Address)
if err != nil { if err != nil {
return err return err
} }
s.listener = ln s.listener = ln
// 启动接受连接循环
s.wg.Add(1) s.wg.Add(1)
go s.acceptLoop() go s.acceptLoop()
// 启动连接管理 // 启动连接管理(清理空闲连接)
s.wg.Add(1) s.wg.Add(1)
go s.manageConnections() go s.manageConnections()
@ -95,13 +113,17 @@ func (s *HighConcurrentServer) startTCP() error {
} }
// startUDP 启动UDP服务器 // startUDP 启动UDP服务器
// 创建UDP包连接并启动读取循环
// 返回可能的启动错误
func (s *HighConcurrentServer) startUDP() error { func (s *HighConcurrentServer) startUDP() error {
// 创建UDP包连接
pc, err := net.ListenPacket(s.config.Network, s.config.Address) pc, err := net.ListenPacket(s.config.Network, s.config.Address)
if err != nil { if err != nil {
return err return err
} }
s.packetConn = pc s.packetConn = pc
// 启动UDP读取循环
s.wg.Add(1) s.wg.Add(1)
go s.udpReadLoop() go s.udpReadLoop()
@ -109,31 +131,37 @@ func (s *HighConcurrentServer) startUDP() error {
} }
// acceptLoop TCP接受连接循环 // acceptLoop TCP接受连接循环
// 持续接受新的TCP连接并为每个连接创建处理协程
// 当收到关闭信号或发生错误时退出
func (s *HighConcurrentServer) acceptLoop() { func (s *HighConcurrentServer) acceptLoop() {
defer s.wg.Done() defer s.wg.Done()
for { for {
// 检查是否收到关闭信号
select { select {
case <-s.shutdown: case <-s.shutdown:
return return
default: default:
} }
// 接受新连接
conn, err := s.listener.Accept() conn, err := s.listener.Accept()
if err != nil { if err != nil {
// 处理临时错误,如"too many open files"
if ne, ok := err.(net.Error); ok && ne.Temporary() { if ne, ok := err.(net.Error); ok && ne.Temporary() {
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond) // 短暂休眠后重试
continue continue
} }
return return // 非临时错误,退出循环
} }
// 检查最大连接数 // 检查是否超过最大连接数限制
if atomic.LoadInt64(&s.activeConns) >= int64(s.config.MaxConn) { if atomic.LoadInt64(&s.activeConns) >= int64(s.config.MaxConn) {
conn.Close() conn.Close() // 超过限制,直接关闭连接
continue continue
} }
// 增加活动连接计数并启动连接处理协程
atomic.AddInt64(&s.activeConns, 1) atomic.AddInt64(&s.activeConns, 1)
s.wg.Add(1) s.wg.Add(1)
go s.handleNewConnection(conn) go s.handleNewConnection(conn)
@ -141,67 +169,79 @@ func (s *HighConcurrentServer) acceptLoop() {
} }
// handleNewConnection 处理新连接 // handleNewConnection 处理新连接
// conn: 新接受的TCP连接
// 为新连接创建Connection对象启动读写循环并管理连接的生命周期
func (s *HighConcurrentServer) handleNewConnection(conn net.Conn) { func (s *HighConcurrentServer) handleNewConnection(conn net.Conn) {
defer s.wg.Done() defer s.wg.Done()
defer atomic.AddInt64(&s.activeConns, -1) defer atomic.AddInt64(&s.activeConns, -1) // 减少活动连接计数
defer conn.Close() defer conn.Close() // 确保连接关闭
// 获取分片 // 获取连接对应的分片,用于减少锁竞争
shard := s.getShard(conn) shard := s.getShard(conn)
c := &Connection{ c := &Connection{
Conn: conn, Conn: conn,
readBuffer: bytes.NewBuffer(make([]byte, 0, 4096)), readBuffer: bytes.NewBuffer(make([]byte, 0, 4096)), // 初始化读缓冲区容量为4KB
writeBuffer: bytes.NewBuffer(make([]byte, 0, 4096)), writeBuffer: bytes.NewBuffer(make([]byte, 0, 4096)), // 初始化写缓冲区容量为4KB
writeChan: make(chan []byte, 32), writeChan: make(chan []byte, 32), // 写入通道缓冲大小为32
closeChan: make(chan struct{}), closeChan: make(chan struct{}), // 关闭信号通道
server: s, server: s,
lastActive: time.Now(), lastActive: time.Now(), // 记录初始活动时间
} }
// 添加到连接管理 // 将连接添加到分片管理中
shard.addConn(c) shard.addConn(c)
// 启动读写协程 // 启动读写协程
go c.readLoop() go c.readLoop() // 读取循环在单独的协程中运行
c.writeLoop() c.writeLoop() // 写入循环在当前协程中运行
// 等待关闭 // 等待关闭信号
<-c.closeChan <-c.closeChan
shard.removeConn(c) shard.removeConn(c) // 从分片中移除连接
} }
// getShard 获取连接对应的分片 // getShard 获取连接对应的分片
// conn: 网络连接
// 返回该连接应该被分配到的分片
// 使用连接的远程IP地址进行哈希来确定分片以实现负载均衡
func (s *HighConcurrentServer) getShard(conn net.Conn) *connectionShard { func (s *HighConcurrentServer) getShard(conn net.Conn) *connectionShard {
// 使用连接的远程地址作为分片键 // 尝试获取TCP地址
addr, ok := conn.RemoteAddr().(*net.TCPAddr) addr, ok := conn.RemoteAddr().(*net.TCPAddr)
if !ok { if !ok {
return s.shards[0] return s.shards[0] // 非TCP连接返回第一个分片
} }
// 简单哈希算法 // 使用IP地址的四个字节进行简单哈希
// 将IP地址的四个字节相加作为哈希值
hash := addr.IP.To4()[0] + addr.IP.To4()[1] + addr.IP.To4()[2] + addr.IP.To4()[3] hash := addr.IP.To4()[0] + addr.IP.To4()[1] + addr.IP.To4()[2] + addr.IP.To4()[3]
return s.shards[int(hash)%s.shardCount] return s.shards[int(hash)%s.shardCount] // 使用取模运算确定分片索引
} }
// udpReadLoop UDP读取循环 // udpReadLoop UDP读取循环
// 持续从UDP连接读取数据包并处理
// 使用sync.Pool复用缓冲区以减少内存分配
func (s *HighConcurrentServer) udpReadLoop() { func (s *HighConcurrentServer) udpReadLoop() {
defer s.wg.Done() defer s.wg.Done()
defer s.packetConn.Close() defer s.packetConn.Close()
// 创建缓冲区池每个缓冲区大小为64KBUDP最大包大小
bufPool := sync.Pool{ bufPool := sync.Pool{
New: func() interface{} { return make([]byte, 65536) }, New: func() interface{} { return make([]byte, 65536) },
} }
for { for {
// 检查是否收到关闭信号
select { select {
case <-s.shutdown: case <-s.shutdown:
return return
default: default:
} }
// 从池中获取缓冲区
buf := bufPool.Get().([]byte) buf := bufPool.Get().([]byte)
n, addr, err := s.packetConn.ReadFrom(buf) n, addr, err := s.packetConn.ReadFrom(buf)
if err != nil { if err != nil {
// 处理临时错误
if ne, ok := err.(net.Error); ok && ne.Temporary() { if ne, ok := err.(net.Error); ok && ne.Temporary() {
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
continue continue
@ -209,18 +249,19 @@ func (s *HighConcurrentServer) udpReadLoop() {
return return
} }
// 启动新的协程处理数据包
s.wg.Add(1) s.wg.Add(1)
go func(data []byte, addr net.Addr) { go func(data []byte, addr net.Addr) {
defer s.wg.Done() defer s.wg.Done()
defer bufPool.Put(data) defer bufPool.Put(data) // 将缓冲区放回池中
// 处理UDP数据包 // 解码数据包
packet := s.packetType() packet := s.packetType()
if err := packet.Decode(data[:n]); err != nil { if err := packet.Decode(data[:n]); err != nil {
return return
} }
// 创建虚拟连接 // 创建虚拟UDP连接
conn := &udpConn{PacketConn: s.packetConn, addr: addr} conn := &udpConn{PacketConn: s.packetConn, addr: addr}
// 调用用户处理函数 // 调用用户处理函数
@ -229,7 +270,7 @@ func (s *HighConcurrentServer) udpReadLoop() {
return return
} }
// 发送响应 // 发送响应(如果有)
if response != nil { if response != nil {
respData, err := response.Encode() respData, err := response.Encode()
if err == nil { if err == nil {
@ -241,9 +282,12 @@ func (s *HighConcurrentServer) udpReadLoop() {
} }
// manageConnections 管理连接(关闭空闲连接) // manageConnections 管理连接(关闭空闲连接)
// 定期检查并关闭超过空闲超时时间的连接
// 当服务器关闭时退出
func (s *HighConcurrentServer) manageConnections() { func (s *HighConcurrentServer) manageConnections() {
defer s.wg.Done() defer s.wg.Done()
// 创建定时器每30秒检查一次
ticker := time.NewTicker(30 * time.Second) ticker := time.NewTicker(30 * time.Second)
defer ticker.Stop() defer ticker.Stop()
@ -252,16 +296,21 @@ func (s *HighConcurrentServer) manageConnections() {
case <-s.shutdown: case <-s.shutdown:
return return
case <-ticker.C: case <-ticker.C:
// 如果未设置空闲超时,跳过检查
if s.config.IdleTimeout <= 0 { if s.config.IdleTimeout <= 0 {
continue continue
} }
now := time.Now() now := time.Now()
// 遍历所有分片
for _, shard := range s.shards { for _, shard := range s.shards {
shard.lock.RLock() shard.lock.RLock()
// 遍历分片中的所有连接
shard.conns.Range(func(key, value interface{}) bool { shard.conns.Range(func(key, value interface{}) bool {
conn := value.(*Connection) conn := value.(*Connection)
// 检查连接是否超过空闲超时时间
if now.Sub(conn.lastActive) > s.config.IdleTimeout { if now.Sub(conn.lastActive) > s.config.IdleTimeout {
// 发送关闭信号
select { select {
case conn.closeChan <- struct{}{}: case conn.closeChan <- struct{}{}:
default: default: