wip: 又又加了一些新东西。
parent
6ec0070ebe
commit
115166cb11
@ -0,0 +1,18 @@
|
||||
package message
|
||||
|
||||
type BinarySerializer struct {
|
||||
}
|
||||
|
||||
func NewBinarySerializer() Serializer {
|
||||
return &BinarySerializer{}
|
||||
}
|
||||
|
||||
func (b *BinarySerializer) Marshal(i interface{}) ([]byte, error) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (b *BinarySerializer) Unmarshal(bytes []byte, i interface{}) error {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package nface
|
||||
|
||||
import "net"
|
||||
|
||||
const (
|
||||
// StatusStart 开始阶段
|
||||
StatusStart int32 = iota + 1
|
||||
// StatusPrepare 准备阶段
|
||||
StatusPrepare
|
||||
// StatusWorking 工作阶段
|
||||
StatusWorking
|
||||
// StatusClosed 连接关闭
|
||||
StatusClosed
|
||||
)
|
||||
|
||||
type IConnection interface {
|
||||
// Status 获取连接状态
|
||||
Status() int32
|
||||
// SetStatus 设置连接状态
|
||||
SetStatus(s int32)
|
||||
// Conn 获取底层网络连接
|
||||
Conn() net.Conn
|
||||
// ID 获取连接ID
|
||||
ID() int64
|
||||
// Session 获取当前连接绑定的Session
|
||||
Session() ISession
|
||||
// Close 关闭连接
|
||||
Close() error
|
||||
}
|
@ -0,0 +1,164 @@
|
||||
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()
|
||||
}
|
@ -0,0 +1 @@
|
||||
package nnet
|
@ -1,63 +0,0 @@
|
||||
package nnet
|
||||
|
||||
import (
|
||||
"git.noahlan.cn/northlan/nnet/nface"
|
||||
"git.noahlan.cn/northlan/nnet/pipeline"
|
||||
"git.noahlan.cn/northlan/nnet/session"
|
||||
"github.com/gorilla/websocket"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Request struct {
|
||||
session nface.ISession // Session
|
||||
|
||||
conn net.Conn // low-level conn fd
|
||||
status Status // 连接状态
|
||||
lastMid uint64 // 最近一次消息ID
|
||||
lastHeartbeatAt int64 // 最近一次心跳时间
|
||||
|
||||
chDie chan struct{} // 停止通道
|
||||
chSend chan []byte // 消息发送通道
|
||||
|
||||
pipeline pipeline.Pipeline // 消息管道
|
||||
}
|
||||
|
||||
func newRequest(conn net.Conn, pipeline pipeline.Pipeline) *Request {
|
||||
r := &Request{
|
||||
conn: conn,
|
||||
status: StatusStart,
|
||||
|
||||
lastHeartbeatAt: time.Now().Unix(),
|
||||
|
||||
chDie: make(chan struct{}),
|
||||
chSend: make(chan []byte),
|
||||
|
||||
pipeline: pipeline,
|
||||
}
|
||||
|
||||
// binding session
|
||||
r.session = session.New()
|
||||
return r
|
||||
}
|
||||
|
||||
func newRequestWS(conn *websocket.Conn, pipeline pipeline.Pipeline) *Request {
|
||||
c, err := newWSConn(conn)
|
||||
if err != nil {
|
||||
// TODO panic ?
|
||||
panic(err)
|
||||
}
|
||||
return newRequest(c, pipeline)
|
||||
}
|
||||
|
||||
func (r *Request) Status() Status {
|
||||
return r.status
|
||||
}
|
||||
|
||||
func (r *Request) ID() int64 {
|
||||
return r.session.ID()
|
||||
}
|
||||
|
||||
func (r *Request) Session() nface.ISession {
|
||||
return r.session
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package nnet
|
||||
|
||||
type Status uint8
|
||||
|
||||
const (
|
||||
// StatusStart 开始阶段
|
||||
StatusStart Status = iota + 1
|
||||
// StatusPrepare 准备阶段
|
||||
StatusPrepare
|
||||
// StatusWorking 工作阶段
|
||||
StatusWorking
|
||||
// StatusClosed 连接关闭
|
||||
StatusClosed
|
||||
)
|
@ -0,0 +1,23 @@
|
||||
package packet
|
||||
|
||||
import "git.noahlan.cn/northlan/nnet/nface"
|
||||
|
||||
// Type 数据帧类型,如:握手,心跳,数据等
|
||||
type Type byte
|
||||
|
||||
type (
|
||||
Packer interface {
|
||||
// Pack 从原始raw bytes创建一个用于网络传输的 packet.Packet 结构
|
||||
Pack(typ Type, data []byte) ([]byte, error)
|
||||
|
||||
// Unpack 解包
|
||||
Unpack(data []byte) ([]interface{}, error)
|
||||
}
|
||||
|
||||
// Processor 数据帧处理器,拆包之后的处理
|
||||
Processor interface {
|
||||
// ProcessPacket 单个数据包处理方法
|
||||
// packet 为实际数据包,是 packet.Packer 的Unpack方法拆包出来的数据指针
|
||||
ProcessPacket(conn nface.IConnection, packet interface{}) error
|
||||
}
|
||||
)
|
@ -0,0 +1,40 @@
|
||||
package packet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.noahlan.cn/northlan/nnet/log"
|
||||
"git.noahlan.cn/northlan/nnet/nface"
|
||||
)
|
||||
|
||||
type DefaultProcessor struct{}
|
||||
|
||||
func NewDefaultProcessor() *DefaultProcessor {
|
||||
return &DefaultProcessor{}
|
||||
}
|
||||
|
||||
func (d *DefaultProcessor) ProcessPacket(conn nface.IConnection, packet interface{}) error {
|
||||
p := packet.(*Packet)
|
||||
switch p.Type {
|
||||
case Handshake:
|
||||
// TODO validate handshake
|
||||
if _, err := conn.Conn().Write([]byte{}); err != nil {
|
||||
return err
|
||||
}
|
||||
conn.SetStatus(nface.StatusPrepare)
|
||||
log.Debugf("Connection handshake Id=%d, Remote=%s", conn.ID(), conn.Conn().RemoteAddr())
|
||||
case HandshakeAck:
|
||||
conn.SetStatus(nface.StatusWorking)
|
||||
log.Debugf("Receive handshake ACK Id=%d, Remote=%s", conn.ID(), conn.Conn().RemoteAddr())
|
||||
|
||||
case Data:
|
||||
if conn.Status() < nface.StatusWorking {
|
||||
return fmt.Errorf("receive data on socket which not yet ACK, session will be closed immediately, remote=%s",
|
||||
conn.Conn().RemoteAddr())
|
||||
}
|
||||
// TODO message data 处理
|
||||
case Heartbeat:
|
||||
// expected
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue