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