You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nnet-old/nnet/connection.go

165 lines
3.5 KiB
Go

package nnet
import (
"errors"
"git.noahlan.cn/northlan/nnet/log"
"git.noahlan.cn/northlan/nnet/nface"
"git.noahlan.cn/northlan/nnet/packet"
"git.noahlan.cn/northlan/nnet/pipeline"
"git.noahlan.cn/northlan/nnet/session"
"github.com/gorilla/websocket"
"net"
"sync/atomic"
"time"
)
var (
ErrCloseClosedSession = errors.New("close closed session")
)
type (
Connection struct {
session nface.ISession // Session
server *Server // Server 引用
conn net.Conn // low-level conn fd
status int32 // 连接状态
lastMid uint64 // 最近一次消息ID
lastHeartbeatAt int64 // 最近一次心跳时间
chDie chan struct{} // 停止通道
chSend chan []byte // 消息发送通道
pipeline pipeline.Pipeline // 消息管道
}
pendingMessage struct {
typ byte // message type
route string // message route
mid uint64 // response message id
payload interface{} // payload
}
)
func newConnection(server *Server, conn net.Conn, pipeline pipeline.Pipeline) nface.IConnection {
r := &Connection{
conn: conn,
server: server,
status: nface.StatusStart,
lastHeartbeatAt: time.Now().Unix(),
chDie: make(chan struct{}),
chSend: make(chan pendingMessage, 2048),
pipeline: pipeline,
}
// binding session
r.session = session.New()
return r
}
func newConnectionWS(server *Server, conn *websocket.Conn, pipeline pipeline.Pipeline) nface.IConnection {
c, err := newWSConn(conn)
if err != nil {
// TODO panic ?
panic(err)
}
return newConnection(server, c, pipeline)
}
func (r *Connection) Status() int32 {
return atomic.LoadInt32(&r.status)
}
func (r *Connection) SetStatus(s int32) {
atomic.StoreInt32(&r.status, s)
}
func (r *Connection) Conn() net.Conn {
return r.conn
}
func (r *Connection) ID() int64 {
return r.session.ID()
}
func (r *Connection) Session() nface.ISession {
return r.session
}
func (r *Connection) write() {
ticker := time.NewTicker(r.server.HeartbeatInterval)
chWrite := make(chan []byte, 1024)
defer func() {
ticker.Stop()
close(r.chSend)
close(chWrite)
_ = r.Close()
log.Debugf("Session write goroutine exit, SessionID=%d, UID=%d", r.session.ID(), r.session.UID())
}()
for {
select {
case <-ticker.C:
deadline := time.Now().Add(-2 * r.server.HeartbeatInterval).Unix()
if atomic.LoadInt64(&r.lastHeartbeatAt) < deadline {
log.Debugf("Session heartbeat timeout, LastTime=%d, Deadline=%d", atomic.LoadInt64(&r.lastHeartbeatAt), deadline)
return
}
// TODO heartbeat data
chWrite <- []byte{}
case data := <-r.chSend:
// message marshal data
payload, err := r.server.Serializer.Marshal(data.payload)
if err != nil {
switch data.typ {
}
break
}
// TODO new message and pipeline
// TODO encode message ? message processor ?
// packet pack data
p, err := r.server.Packer.Pack(packet.Data, payload)
if err != nil {
log.Error(err.Error())
break
}
chWrite <- p
case data := <-chWrite:
// 回写数据
if _, err := r.conn.Write(data); err != nil {
log.Error(err.Error())
return
}
case <-r.chDie: // connection close signal
return
// TODO application quit signal
}
}
}
func (r *Connection) Close() error {
if r.Status() == StatusClosed {
return ErrCloseClosedSession
}
r.SetStatus(StatusClosed)
log.Debugf("close connection, ID: %d", r.ID())
select {
case <-r.chDie:
default:
close(r.chDie)
// TODO lifetime
}
return r.conn.Close()
}