feat(mpsync): 多人同步 MVP——登录区块流、方块变更广播;修复 netserver 关闭竞态与世界 worker 退出死锁
This commit is contained in:
@@ -10,6 +10,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@@ -112,12 +113,13 @@ type Conn struct {
|
||||
conn net.Conn
|
||||
srv *Server
|
||||
send chan []byte
|
||||
done chan struct{}
|
||||
closeOnce sync.Once
|
||||
}
|
||||
|
||||
// newConn 创建连接。
|
||||
func newConn(id uint64, nc net.Conn, s *Server) *Conn {
|
||||
return &Conn{id: id, conn: nc, srv: s, send: make(chan []byte, 64)}
|
||||
return &Conn{id: id, conn: nc, srv: s, send: make(chan []byte, 64), done: make(chan struct{})}
|
||||
}
|
||||
|
||||
// ID 连接 ID(服务器分配,多人同步.md §3)。
|
||||
@@ -186,29 +188,41 @@ func (c *Conn) handle(f netproto.Frame) {
|
||||
|
||||
// writeLoop 写循环:channel 驱动,写超时断开(网络协议.md §7)。
|
||||
func (c *Conn) writeLoop() {
|
||||
for data := range c.send {
|
||||
_ = c.conn.SetWriteDeadline(time.Now().Add(readTimeout))
|
||||
if _, err := c.conn.Write(data); err != nil {
|
||||
c.close()
|
||||
for {
|
||||
select {
|
||||
case <-c.done:
|
||||
return
|
||||
case data := <-c.send:
|
||||
_ = c.conn.SetWriteDeadline(time.Now().Add(readTimeout))
|
||||
if _, err := c.conn.Write(data); err != nil {
|
||||
c.close()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send 发送一帧(非阻塞入队;队满丢弃并告警,防慢连接阻塞服务器)。
|
||||
// Send 发送一帧(非阻塞入队;连接关闭或队满时丢弃并告警,防慢连接阻塞服务器)。
|
||||
func (c *Conn) Send(f netproto.Frame) {
|
||||
select {
|
||||
case <-c.done:
|
||||
return
|
||||
default:
|
||||
}
|
||||
select {
|
||||
case c.send <- netproto.EncodeFrame(f):
|
||||
case <-c.done:
|
||||
default:
|
||||
c.srv.log.Warnf("连接 %d 发送队列已满,丢弃帧 0x%02X", c.id, f.MsgID)
|
||||
}
|
||||
}
|
||||
|
||||
// close 幂等关闭。
|
||||
// close 幂等关闭:关闭 done 信号与 socket;不关闭 send(避免与在途 Send 竞争)。
|
||||
func (c *Conn) close() {
|
||||
c.closeOnce.Do(func() {
|
||||
c.srv.log.Debugf("连接 %d 关闭(来源:\n%s)", c.id, debug.Stack())
|
||||
close(c.done)
|
||||
_ = c.conn.Close()
|
||||
close(c.send)
|
||||
c.srv.mu.Lock()
|
||||
delete(c.srv.conns, c.id)
|
||||
c.srv.mu.Unlock()
|
||||
|
||||
Reference in New Issue
Block a user