parent
e9bb90ba8c
commit
d39b9921cf
@ -1,23 +1,23 @@
|
|||||||
package nnet
|
package nnet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.noahlan.cn/noahlan/nnet/connection"
|
"git.noahlan.cn/noahlan/nnet/conn"
|
||||||
"git.noahlan.cn/noahlan/ntool/nlog"
|
"git.noahlan.cn/noahlan/ntool/nlog"
|
||||||
"net"
|
"net"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DialTCP 连接服务器
|
// DialTCP 连接服务器
|
||||||
func (ngin *Engine) DialTCP(addr string) (*connection.Connection, error) {
|
func (ngin *Engine) DialTCP(addr string) (*conn.Connection, error) {
|
||||||
err := ngin.setup()
|
err := ngin.setup()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
nlog.Errorf("%s failed to setup server, err:%v", ngin.LogPrefix(), err)
|
nlog.Errorf("%s failed to setup server, err:%v", ngin.LogPrefix(), err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := net.Dial("tcp", addr)
|
rc, err := net.Dial("tcp", addr)
|
||||||
nlog.Must(err)
|
nlog.Must(err)
|
||||||
|
|
||||||
nlog.Infof("%s now connect to %s...", ngin.LogPrefix(), addr)
|
nlog.Infof("%s now connect to %s...", ngin.LogPrefix(), addr)
|
||||||
|
|
||||||
return ngin.handle(conn), nil
|
return ngin.handle(rc), nil
|
||||||
}
|
}
|
||||||
|
@ -1 +1,65 @@
|
|||||||
package nnet
|
package nnet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"git.noahlan.cn/noahlan/nnet/config"
|
||||||
|
"git.noahlan.cn/noahlan/nnet/conn"
|
||||||
|
"git.noahlan.cn/noahlan/ntool/nlog"
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/jpillora/backoff"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DialWebsocket websocket方式 连接服务器
|
||||||
|
func (ngin *Engine) DialWebsocket(url string, conf config.WSClientFullConf, evtOpts ...WsEventOption) (*conn.Connection, error) {
|
||||||
|
for _, opt := range evtOpts {
|
||||||
|
opt(conf.WSEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
ngin.ReadDeadline = conf.ReadDeadline
|
||||||
|
ngin.WriteDeadline = conf.WriteDeadline
|
||||||
|
|
||||||
|
err := ngin.setup()
|
||||||
|
if err != nil {
|
||||||
|
nlog.Errorf("%s failed to setup server, err:%v", ngin.LogPrefix(), err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dialer := websocket.Dialer{
|
||||||
|
TLSClientConfig: &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
HandshakeTimeout: conf.HandshakeTimeout,
|
||||||
|
ReadBufferSize: conf.ReadBufferSize,
|
||||||
|
WriteBufferSize: conf.WriteBufferSize,
|
||||||
|
EnableCompression: conf.Compression,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 连接重试
|
||||||
|
b := &backoff.Backoff{
|
||||||
|
Factor: conf.RecFactor,
|
||||||
|
Jitter: true,
|
||||||
|
Min: conf.MinRecTime,
|
||||||
|
Max: conf.MaxRecTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
var wsConn *websocket.Conn
|
||||||
|
for {
|
||||||
|
nextRec := b.Duration()
|
||||||
|
wsConn, _, err = dialer.Dial(url, http.Header{})
|
||||||
|
if err != nil {
|
||||||
|
ngin.evtMgr.OnConnectError(err)
|
||||||
|
time.Sleep(nextRec)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if conf.ReadLimit != 0 {
|
||||||
|
wsConn.SetReadLimit(conf.ReadLimit)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
nlog.Infof("%s now connect to %s...", ngin.LogPrefix(), url)
|
||||||
|
|
||||||
|
return ngin.handleWS(wsConn, conf.WSEvent), nil
|
||||||
|
}
|
||||||
|
@ -0,0 +1,147 @@
|
|||||||
|
package conn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.noahlan.cn/noahlan/nnet/session"
|
||||||
|
"net"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
Connection struct {
|
||||||
|
session *session.Session // Session
|
||||||
|
|
||||||
|
status int32 // 连接状态
|
||||||
|
conn net.Conn // low-level conn fd
|
||||||
|
typ ConnType // 连接类型
|
||||||
|
|
||||||
|
lastMid uint64 // 最近一次消息ID
|
||||||
|
|
||||||
|
chDie chan struct{} // 停止通道
|
||||||
|
chSend chan PendingMessage // 消息发送通道(结构化消息)
|
||||||
|
chWrite chan []byte // 消息发送通道(二进制消息)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewConnection(id int64, rawC net.Conn) *Connection {
|
||||||
|
r := &Connection{
|
||||||
|
session: session.NewSession(id),
|
||||||
|
|
||||||
|
status: StatusStart,
|
||||||
|
conn: rawC,
|
||||||
|
typ: ConnTypeTCP,
|
||||||
|
|
||||||
|
lastMid: 0,
|
||||||
|
|
||||||
|
chDie: make(chan struct{}),
|
||||||
|
chSend: make(chan PendingMessage, 128),
|
||||||
|
chWrite: make(chan []byte, 128),
|
||||||
|
}
|
||||||
|
if _, ok := rawC.(*WSConn); ok {
|
||||||
|
r.typ = ConnTypeWS
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
if _, ok := rawC.(*SerialConn); ok {
|
||||||
|
r.typ = ConnTypeSerial
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Connection) Send(header, payload any) (err error) {
|
||||||
|
defer func() {
|
||||||
|
if e := recover(); e != nil {
|
||||||
|
err = ErrBrokenPipe
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
r.chSend <- PendingMessage{
|
||||||
|
Header: header,
|
||||||
|
Payload: payload,
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Connection) SendBytes(data []byte) (err error) {
|
||||||
|
defer func() {
|
||||||
|
if e := recover(); e != nil {
|
||||||
|
err = ErrBrokenPipe
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
r.chWrite <- data
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Connection) Status() int32 {
|
||||||
|
return atomic.LoadInt32(&r.status)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Connection) SetStatus(s int32) {
|
||||||
|
atomic.StoreInt32(&r.status, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Connection) Type() ConnType {
|
||||||
|
return r.typ
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Connection) Conn() net.Conn {
|
||||||
|
return r.conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Connection) WsConn() *WSConn {
|
||||||
|
if r.typ == ConnTypeWS {
|
||||||
|
return r.conn.(*WSConn)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Connection) SerialConn() *SerialConn {
|
||||||
|
if r.typ == ConnTypeSerial {
|
||||||
|
return r.conn.(*SerialConn)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Connection) ID() int64 {
|
||||||
|
return r.session.ID()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Connection) Session() *session.Session {
|
||||||
|
return r.session
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Connection) LastMID() uint64 {
|
||||||
|
return r.lastMid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Connection) SetLastMID(mid uint64) {
|
||||||
|
atomic.StoreUint64(&r.lastMid, mid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Connection) ChDie() chan struct{} {
|
||||||
|
return r.chDie
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Connection) ChSend() chan PendingMessage {
|
||||||
|
return r.chSend
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Connection) ChWrite() chan []byte {
|
||||||
|
return r.chWrite
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Connection) Close() error {
|
||||||
|
if r.Status() == StatusClosed {
|
||||||
|
return ErrCloseClosedSession
|
||||||
|
}
|
||||||
|
r.SetStatus(StatusClosed)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-r.chDie:
|
||||||
|
close(r.chSend)
|
||||||
|
close(r.chWrite)
|
||||||
|
default:
|
||||||
|
close(r.chDie)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.session.Close()
|
||||||
|
return r.conn.Close()
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package conn
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrCloseClosedSession = errors.New("close closed session")
|
||||||
|
// ErrBrokenPipe represents the low-level connection has broken.
|
||||||
|
ErrBrokenPipe = errors.New("broken low-level pipe")
|
||||||
|
|
||||||
|
ErrSendPayload = errors.New("serializer is nil, but payload type not []byte")
|
||||||
|
ErrSendMarshal = errors.New("message body marshal err")
|
||||||
|
ErrSend = errors.New("send err")
|
||||||
|
ErrSendWSType = errors.New("websocket message type err")
|
||||||
|
|
||||||
|
ErrPack = errors.New("pack err")
|
||||||
|
ErrUnpack = errors.New("unPacker err")
|
||||||
|
ErrNoPacker = errors.New("no packer")
|
||||||
|
|
||||||
|
ErrReceiveZero = errors.New("receive zero")
|
||||||
|
)
|
@ -1,4 +1,4 @@
|
|||||||
package connection
|
package conn
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
@ -1,4 +1,4 @@
|
|||||||
package connection
|
package conn
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.noahlan.cn/noahlan/ntool/nlog"
|
"git.noahlan.cn/noahlan/ntool/nlog"
|
@ -1,4 +1,4 @@
|
|||||||
package connection
|
package conn
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
@ -0,0 +1,27 @@
|
|||||||
|
package conn
|
||||||
|
|
||||||
|
const (
|
||||||
|
// StatusStart 开始阶段
|
||||||
|
StatusStart int32 = iota + 1
|
||||||
|
// StatusPrepare 准备阶段
|
||||||
|
StatusPrepare
|
||||||
|
// StatusPending 等待工作阶段
|
||||||
|
StatusPending
|
||||||
|
// StatusWorking 工作阶段
|
||||||
|
StatusWorking
|
||||||
|
// StatusClosed 连接关闭
|
||||||
|
StatusClosed
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConnType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ConnTypeTCP ConnType = iota // TCP connection
|
||||||
|
ConnTypeWS // Websocket connection
|
||||||
|
ConnTypeSerial // Websocket connection
|
||||||
|
)
|
||||||
|
|
||||||
|
type PendingMessage struct {
|
||||||
|
Header any
|
||||||
|
Payload any
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package connection
|
package conn
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
@ -1,83 +0,0 @@
|
|||||||
package connection
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
Func func(c *Connection, v any) error
|
|
||||||
|
|
||||||
// Pipeline 消息管道
|
|
||||||
Pipeline interface {
|
|
||||||
Outbound() Channel
|
|
||||||
Inbound() Channel
|
|
||||||
}
|
|
||||||
|
|
||||||
pipeline struct {
|
|
||||||
outbound, inbound *pipelineChannel
|
|
||||||
}
|
|
||||||
|
|
||||||
Channel interface {
|
|
||||||
PushFront(h Func)
|
|
||||||
PushBack(h Func)
|
|
||||||
Process(c *Connection, v any) error
|
|
||||||
}
|
|
||||||
|
|
||||||
pipelineChannel struct {
|
|
||||||
mu sync.RWMutex
|
|
||||||
handlers []Func
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewPipeline() Pipeline {
|
|
||||||
return &pipeline{
|
|
||||||
outbound: &pipelineChannel{},
|
|
||||||
inbound: &pipelineChannel{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *pipeline) Outbound() Channel {
|
|
||||||
return p.outbound
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *pipeline) Inbound() Channel {
|
|
||||||
return p.inbound
|
|
||||||
}
|
|
||||||
|
|
||||||
// PushFront 将func压入slice首位
|
|
||||||
func (p *pipelineChannel) PushFront(h Func) {
|
|
||||||
p.mu.Lock()
|
|
||||||
defer p.mu.Unlock()
|
|
||||||
|
|
||||||
handlers := make([]Func, len(p.handlers)+1)
|
|
||||||
handlers[0] = h
|
|
||||||
copy(handlers[1:], p.handlers)
|
|
||||||
|
|
||||||
p.handlers = handlers
|
|
||||||
}
|
|
||||||
|
|
||||||
// PushBack 将func压入slice末位
|
|
||||||
func (p *pipelineChannel) PushBack(h Func) {
|
|
||||||
p.mu.Lock()
|
|
||||||
defer p.mu.Unlock()
|
|
||||||
|
|
||||||
p.handlers = append(p.handlers, h)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process 处理所有的pipeline方法
|
|
||||||
func (p *pipelineChannel) Process(c *Connection, v any) error {
|
|
||||||
if len(p.handlers) < 1 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
p.mu.RLock()
|
|
||||||
defer p.mu.RUnlock()
|
|
||||||
|
|
||||||
for _, handler := range p.handlers {
|
|
||||||
err := handler(c, v)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -0,0 +1,292 @@
|
|||||||
|
package event
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"git.noahlan.cn/noahlan/nnet/conn"
|
||||||
|
"git.noahlan.cn/noahlan/nnet/packet"
|
||||||
|
"git.noahlan.cn/noahlan/ntool/nlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrEventTypeIllegal = errors.New("EventType illegal")
|
||||||
|
|
||||||
|
type EvtType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
EvtOnConnected = "OnConnected"
|
||||||
|
EvtOnConnectError = "OnConnectError"
|
||||||
|
EvtOnDisconnected = "OnDisconnected"
|
||||||
|
EvtOnClose = "OnClose"
|
||||||
|
|
||||||
|
EvtOnSend = "OnSend"
|
||||||
|
EvtOnSendError = "OnSendError"
|
||||||
|
EvtOnReceive = "OnReceive"
|
||||||
|
EvtOnReceiveError = "OnReceiveError"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
OnConnectedFn func(nc *conn.Connection)
|
||||||
|
OnConnectErrorFn func(err error)
|
||||||
|
OnDisconnectedFn func(nc *conn.Connection, err error)
|
||||||
|
OnCloseFn func(conn *conn.Connection)
|
||||||
|
|
||||||
|
OnSendFn func(nc *conn.Connection, v any)
|
||||||
|
OnSendErrorFn func(nc *conn.Connection, v any, err error)
|
||||||
|
OnReceiveFn func(nc *conn.Connection, p packet.IPacket)
|
||||||
|
OnReceiveErrorFn func(nc *conn.Connection, err error)
|
||||||
|
|
||||||
|
Event interface {
|
||||||
|
// OnConnected 连接成功回调
|
||||||
|
OnConnected(nc *conn.Connection)
|
||||||
|
// OnConnectError 连接异常回调, 在准备进行连接的过程中发生异常时触发
|
||||||
|
OnConnectError(err error)
|
||||||
|
// OnDisconnected 连接断开回调,网络异常,服务端掉线等情况时触发
|
||||||
|
OnDisconnected(nc *conn.Connection, err error)
|
||||||
|
// OnClose 连接关闭回调,服务端发起关闭信号或客户端主动关闭时触发
|
||||||
|
OnClose(nc *conn.Connection)
|
||||||
|
|
||||||
|
// OnSend 消息发送回调,消息序列化后的回调
|
||||||
|
OnSend(nc *conn.Connection, v any)
|
||||||
|
// OnSendError 发送消息异常回调
|
||||||
|
OnSendError(nc *conn.Connection, v any, err error)
|
||||||
|
// OnReceive 消息接收回调,消息解包后的回调
|
||||||
|
OnReceive(nc *conn.Connection, p packet.IPacket)
|
||||||
|
// OnReceiveError 接收消息异常回调
|
||||||
|
OnReceiveError(nc *conn.Connection, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
EventManager interface {
|
||||||
|
Event
|
||||||
|
|
||||||
|
// RegisterEventFront 向头部注册事件处理器
|
||||||
|
RegisterEventFront(evtType EvtType, fn any)
|
||||||
|
// RegisterEvent 注册事件处理器
|
||||||
|
RegisterEvent(evtType EvtType, fn any)
|
||||||
|
}
|
||||||
|
|
||||||
|
eventManager struct {
|
||||||
|
onConnected []OnConnectedFn
|
||||||
|
onConnectError []OnConnectErrorFn
|
||||||
|
onDisconnected []OnDisconnectedFn
|
||||||
|
onClose []OnCloseFn
|
||||||
|
|
||||||
|
onSend []OnSendFn
|
||||||
|
onSendError []OnSendErrorFn
|
||||||
|
onReceive []OnReceiveFn
|
||||||
|
onReceiveError []OnReceiveErrorFn
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
///////////////// type-align
|
||||||
|
var _ Event = (*eventManager)(nil)
|
||||||
|
|
||||||
|
func NewEventManager() EventManager {
|
||||||
|
return &eventManager{
|
||||||
|
onConnected: make([]OnConnectedFn, 0),
|
||||||
|
onConnectError: make([]OnConnectErrorFn, 0),
|
||||||
|
onDisconnected: make([]OnDisconnectedFn, 0),
|
||||||
|
onClose: make([]OnCloseFn, 0),
|
||||||
|
|
||||||
|
onSend: make([]OnSendFn, 0),
|
||||||
|
onSendError: make([]OnSendErrorFn, 0),
|
||||||
|
onReceive: make([]OnReceiveFn, 0),
|
||||||
|
onReceiveError: make([]OnReceiveErrorFn, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *eventManager) registerEvent(evtType EvtType, fn any, front bool) {
|
||||||
|
switch evtType {
|
||||||
|
case EvtOnConnected:
|
||||||
|
if f, ok := fn.(OnConnectedFn); ok {
|
||||||
|
if front {
|
||||||
|
fns := make([]OnConnectedFn, len(m.onConnected)+1)
|
||||||
|
fns[0] = f
|
||||||
|
copy(fns[1:], m.onConnected)
|
||||||
|
m.onConnected = fns
|
||||||
|
} else {
|
||||||
|
m.onConnected = append(m.onConnected, f)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nlog.Error(ErrEventTypeIllegal)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case EvtOnConnectError:
|
||||||
|
if f, ok := fn.(OnConnectErrorFn); ok {
|
||||||
|
if front {
|
||||||
|
fns := make([]OnConnectErrorFn, len(m.onConnectError)+1)
|
||||||
|
fns[0] = f
|
||||||
|
copy(fns[1:], m.onConnectError)
|
||||||
|
m.onConnectError = fns
|
||||||
|
} else {
|
||||||
|
m.onConnectError = append(m.onConnectError, f)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nlog.Error(ErrEventTypeIllegal)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case EvtOnDisconnected:
|
||||||
|
if f, ok := fn.(OnDisconnectedFn); ok {
|
||||||
|
if front {
|
||||||
|
fns := make([]OnDisconnectedFn, len(m.onDisconnected)+1)
|
||||||
|
fns[0] = f
|
||||||
|
copy(fns[1:], m.onDisconnected)
|
||||||
|
m.onDisconnected = fns
|
||||||
|
} else {
|
||||||
|
m.onDisconnected = append(m.onDisconnected, f)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nlog.Error(ErrEventTypeIllegal)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case EvtOnClose:
|
||||||
|
if f, ok := fn.(OnCloseFn); ok {
|
||||||
|
if front {
|
||||||
|
fns := make([]OnCloseFn, len(m.onClose)+1)
|
||||||
|
fns[0] = f
|
||||||
|
copy(fns[1:], m.onClose)
|
||||||
|
m.onClose = fns
|
||||||
|
} else {
|
||||||
|
m.onClose = append(m.onClose, f)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nlog.Error(ErrEventTypeIllegal)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case EvtOnSend:
|
||||||
|
if f, ok := fn.(OnSendFn); ok {
|
||||||
|
if front {
|
||||||
|
fns := make([]OnSendFn, len(m.onSend)+1)
|
||||||
|
fns[0] = f
|
||||||
|
copy(fns[1:], m.onSend)
|
||||||
|
m.onSend = fns
|
||||||
|
} else {
|
||||||
|
m.onSend = append(m.onSend, f)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nlog.Error(ErrEventTypeIllegal)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case EvtOnSendError:
|
||||||
|
if f, ok := fn.(OnSendErrorFn); ok {
|
||||||
|
if front {
|
||||||
|
fns := make([]OnSendErrorFn, len(m.onSendError)+1)
|
||||||
|
fns[0] = f
|
||||||
|
copy(fns[1:], m.onSendError)
|
||||||
|
m.onSendError = fns
|
||||||
|
} else {
|
||||||
|
m.onSendError = append(m.onSendError, f)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nlog.Error(ErrEventTypeIllegal)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case EvtOnReceive:
|
||||||
|
if f, ok := fn.(OnReceiveFn); ok {
|
||||||
|
if front {
|
||||||
|
fns := make([]OnReceiveFn, len(m.onReceive)+1)
|
||||||
|
fns[0] = f
|
||||||
|
copy(fns[1:], m.onReceive)
|
||||||
|
m.onReceive = fns
|
||||||
|
} else {
|
||||||
|
m.onReceive = append(m.onReceive, f)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nlog.Error(ErrEventTypeIllegal)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case EvtOnReceiveError:
|
||||||
|
if f, ok := fn.(OnReceiveErrorFn); ok {
|
||||||
|
if front {
|
||||||
|
fns := make([]OnReceiveErrorFn, len(m.onReceiveError)+1)
|
||||||
|
fns[0] = f
|
||||||
|
copy(fns[1:], m.onReceiveError)
|
||||||
|
m.onReceiveError = fns
|
||||||
|
} else {
|
||||||
|
m.onReceiveError = append(m.onReceiveError, f)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nlog.Error(ErrEventTypeIllegal)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nlog.Infof("Register event [EvtType: %s] successfully", evtType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *eventManager) RegisterEventFront(evtType EvtType, fn any) {
|
||||||
|
m.registerEvent(evtType, fn, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *eventManager) RegisterEvent(evtType EvtType, fn any) {
|
||||||
|
m.registerEvent(evtType, fn, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *eventManager) OnConnected(nc *conn.Connection) {
|
||||||
|
if len(m.onConnected) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, fn := range m.onConnected {
|
||||||
|
fn(nc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *eventManager) OnConnectError(err error) {
|
||||||
|
if len(m.onConnectError) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, fn := range m.onConnectError {
|
||||||
|
fn(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *eventManager) OnDisconnected(nc *conn.Connection, err error) {
|
||||||
|
if len(m.onDisconnected) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, fn := range m.onDisconnected {
|
||||||
|
fn(nc, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *eventManager) OnClose(nc *conn.Connection) {
|
||||||
|
if len(m.onClose) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, fn := range m.onClose {
|
||||||
|
fn(nc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *eventManager) OnSend(nc *conn.Connection, v any) {
|
||||||
|
if len(m.onSend) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, fn := range m.onSend {
|
||||||
|
fn(nc, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *eventManager) OnSendError(nc *conn.Connection, v any, err error) {
|
||||||
|
if len(m.onSendError) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, fn := range m.onSendError {
|
||||||
|
fn(nc, v, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *eventManager) OnReceive(nc *conn.Connection, p packet.IPacket) {
|
||||||
|
if len(m.onReceive) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, fn := range m.onReceive {
|
||||||
|
fn(nc, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *eventManager) OnReceiveError(nc *conn.Connection, err error) {
|
||||||
|
if len(m.onReceiveError) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, fn := range m.onReceiveError {
|
||||||
|
fn(nc, err)
|
||||||
|
}
|
||||||
|
}
|
@ -1,39 +0,0 @@
|
|||||||
package event
|
|
||||||
|
|
||||||
import "git.noahlan.cn/noahlan/nnet/connection"
|
|
||||||
|
|
||||||
type (
|
|
||||||
ConnFn func(conn *connection.Connection)
|
|
||||||
ErrFn func(err error)
|
|
||||||
|
|
||||||
// ConnEvents 连接事件
|
|
||||||
ConnEvents interface {
|
|
||||||
// OnConnected 连接成功回调
|
|
||||||
OnConnected(h ConnFn)
|
|
||||||
// OnConnectError 连接异常回调, 在准备进行连接的过程中发生异常时触发
|
|
||||||
OnConnectError(err error)
|
|
||||||
// OnDisconnected 连接断开回调,网络异常,服务端掉线等情况时触发
|
|
||||||
OnDisconnected(conn *connection.Connection, err error)
|
|
||||||
// OnClose 连接关闭回调,服务端发起关闭信号或客户端主动关闭时触发
|
|
||||||
OnClose(details any, err error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MessageEvents 消息事件
|
|
||||||
MessageEvents interface {
|
|
||||||
// OnSentError 发送消息异常回调
|
|
||||||
OnSentError(details any, err error)
|
|
||||||
// OnReceiveError 接收消息异常回调
|
|
||||||
OnReceiveError(details any, err error)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
type Manager struct {
|
|
||||||
ConnEvents
|
|
||||||
MessageEvents
|
|
||||||
|
|
||||||
onConnected []OnConnectedFn
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewEventManager() *Manager {
|
|
||||||
return &Manager{}
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
package lifetime
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.noahlan.cn/noahlan/nnet/connection"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
Handler func(conn *connection.Connection)
|
|
||||||
|
|
||||||
Lifetime interface {
|
|
||||||
OnClosed(h Handler)
|
|
||||||
OnOpen(h Handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
Mgr struct {
|
|
||||||
onOpen []Handler
|
|
||||||
onClosed []Handler
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewLifetime() *Mgr {
|
|
||||||
return &Mgr{
|
|
||||||
onOpen: make([]Handler, 0),
|
|
||||||
onClosed: make([]Handler, 0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lt *Mgr) OnClosed(h Handler) {
|
|
||||||
lt.onClosed = append(lt.onClosed, h)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lt *Mgr) OnOpen(h Handler) {
|
|
||||||
lt.onOpen = append(lt.onOpen, h)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lt *Mgr) Open(conn *connection.Connection) {
|
|
||||||
if len(lt.onOpen) <= 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, handler := range lt.onOpen {
|
|
||||||
handler(conn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lt *Mgr) Close(conn *connection.Connection) {
|
|
||||||
if len(lt.onClosed) <= 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, handler := range lt.onClosed {
|
|
||||||
handler(conn)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
package packet
|
|
||||||
|
|
||||||
// Entry 入口原始数据
|
|
||||||
type Entry struct {
|
|
||||||
Header any
|
|
||||||
Raw []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewEntry(header any, raw []byte) *Entry {
|
|
||||||
return &Entry{
|
|
||||||
Header: header,
|
|
||||||
Raw: raw,
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,37 @@
|
|||||||
|
package packet
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type WSPacket struct {
|
||||||
|
MessageType int
|
||||||
|
Len uint64
|
||||||
|
Raw []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWSPacket(typ int, data []byte) IPacket {
|
||||||
|
l := len(data)
|
||||||
|
raw := make([]byte, l)
|
||||||
|
copy(raw, data)
|
||||||
|
|
||||||
|
return &WSPacket{
|
||||||
|
MessageType: typ,
|
||||||
|
Len: uint64(l),
|
||||||
|
Raw: raw,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *WSPacket) GetHeader() any {
|
||||||
|
return p.MessageType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *WSPacket) GetLen() uint64 {
|
||||||
|
return p.Len
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *WSPacket) GetBody() []byte {
|
||||||
|
return p.Raw
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *WSPacket) String() string {
|
||||||
|
return fmt.Sprintf("MessageType=%d, Len=%d, RawStr=%s", p.MessageType, p.Len, string(p.Raw))
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
package nnet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"git.noahlan.cn/noahlan/nnet"
|
||||||
|
"git.noahlan.cn/noahlan/nnet/conn"
|
||||||
|
"git.noahlan.cn/noahlan/nnet/event"
|
||||||
|
"git.noahlan.cn/noahlan/nnet/packet"
|
||||||
|
"git.noahlan.cn/noahlan/ntool/nlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OnReadyFunc func()
|
||||||
|
|
||||||
|
func WithNNetClientEvents(onReady OnReadyFunc, packer packet.Packer) nnet.RunOption {
|
||||||
|
return func(ngin *nnet.Engine) {
|
||||||
|
ngin.EventManager().RegisterEventFront(event.EvtOnReceive, onReceiveEvent(ngin, onReady, packer))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func onReceiveEvent(ngin *nnet.Engine, onReady OnReadyFunc, packer packet.Packer) event.OnReceiveFn {
|
||||||
|
return func(nc *conn.Connection, p packet.IPacket) {
|
||||||
|
pkg, ok := p.(*Packet)
|
||||||
|
if !ok {
|
||||||
|
nlog.Error(packet.ErrWrongPacketType)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Server to client
|
||||||
|
switch pkg.PacketType {
|
||||||
|
case Handshake:
|
||||||
|
var handshakeData HandshakeResp
|
||||||
|
err := json.Unmarshal(pkg.Data, &handshakeData)
|
||||||
|
nlog.Must(err)
|
||||||
|
|
||||||
|
hrd, _ := packer.Pack(Header{
|
||||||
|
PacketType: HandshakeAck,
|
||||||
|
MessageHeader: MessageHeader{},
|
||||||
|
}, nil)
|
||||||
|
if err := nc.SendBytes(hrd); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nc.SetStatus(conn.StatusWorking)
|
||||||
|
// onReady
|
||||||
|
if onReady != nil {
|
||||||
|
onReady()
|
||||||
|
}
|
||||||
|
if ngin.ShallLogDebug() {
|
||||||
|
nlog.Debugf("connection handshake Id=%d, Remote=%s", nc.Session().ID(), nc.Conn().RemoteAddr())
|
||||||
|
}
|
||||||
|
case Kick:
|
||||||
|
_ = nc.Close()
|
||||||
|
case Data:
|
||||||
|
status := nc.Status()
|
||||||
|
if status != conn.StatusWorking {
|
||||||
|
nlog.Errorf(fmt.Sprintf("receive data on socket which not yet ACK, session will be closed immediately, remote=%s",
|
||||||
|
nc.Conn().RemoteAddr()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastMid uint64
|
||||||
|
switch pkg.MsgType {
|
||||||
|
case Response:
|
||||||
|
lastMid = pkg.ID
|
||||||
|
case Notify:
|
||||||
|
lastMid = 0
|
||||||
|
}
|
||||||
|
nc.SetLastMID(lastMid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,65 +0,0 @@
|
|||||||
package nnet
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"git.noahlan.cn/noahlan/nnet"
|
|
||||||
"git.noahlan.cn/noahlan/nnet/connection"
|
|
||||||
"git.noahlan.cn/noahlan/nnet/packet"
|
|
||||||
"git.noahlan.cn/noahlan/ntool/nlog"
|
|
||||||
)
|
|
||||||
|
|
||||||
type OnReadyFunc func()
|
|
||||||
|
|
||||||
func WithNNetClientPipeline(onReady OnReadyFunc, packer packet.Packer) nnet.RunOption {
|
|
||||||
return func(ngin *nnet.Engine) {
|
|
||||||
ngin.Pipeline().Inbound().PushFront(func(conn *connection.Connection, v any) error {
|
|
||||||
pkg, ok := v.(*Packet)
|
|
||||||
if !ok {
|
|
||||||
return packet.ErrWrongPacketType
|
|
||||||
}
|
|
||||||
nc, _ := conn.Conn()
|
|
||||||
|
|
||||||
// Server to client
|
|
||||||
switch pkg.PacketType {
|
|
||||||
case Handshake:
|
|
||||||
var handshakeData HandshakeResp
|
|
||||||
err := json.Unmarshal(pkg.Data, &handshakeData)
|
|
||||||
nlog.Must(err)
|
|
||||||
|
|
||||||
hrd, _ := packer.Pack(Header{
|
|
||||||
PacketType: HandshakeAck,
|
|
||||||
MessageHeader: MessageHeader{},
|
|
||||||
}, nil)
|
|
||||||
if err := conn.SendBytes(hrd); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
conn.SetStatus(connection.StatusWorking)
|
|
||||||
// onReady
|
|
||||||
if onReady != nil {
|
|
||||||
onReady()
|
|
||||||
}
|
|
||||||
nlog.Debugf("connection handshake Id=%d, Remote=%s", conn.Session().ID(), nc.RemoteAddr())
|
|
||||||
case Kick:
|
|
||||||
_ = conn.Close()
|
|
||||||
case Data:
|
|
||||||
status := conn.Status()
|
|
||||||
if status != connection.StatusWorking {
|
|
||||||
return errors.New(fmt.Sprintf("receive data on socket which not yet ACK, session will be closed immediately, remote=%s",
|
|
||||||
nc.RemoteAddr()))
|
|
||||||
}
|
|
||||||
|
|
||||||
var lastMid uint64
|
|
||||||
switch pkg.MsgType {
|
|
||||||
case Response:
|
|
||||||
lastMid = pkg.ID
|
|
||||||
case Notify:
|
|
||||||
lastMid = 0
|
|
||||||
}
|
|
||||||
conn.SetLastMID(lastMid)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,85 @@
|
|||||||
|
package nnet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"git.noahlan.cn/noahlan/nnet"
|
||||||
|
"git.noahlan.cn/noahlan/nnet/conn"
|
||||||
|
"git.noahlan.cn/noahlan/nnet/event"
|
||||||
|
"git.noahlan.cn/noahlan/nnet/packet"
|
||||||
|
"git.noahlan.cn/noahlan/ntool/nlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
HandshakeValidatorFunc func(*HandshakeReq) error
|
||||||
|
HandshakeAckPayloadFunc func() any
|
||||||
|
)
|
||||||
|
|
||||||
|
func withNNetEvents(
|
||||||
|
handshakeResp *HandshakeResp,
|
||||||
|
validator HandshakeValidatorFunc,
|
||||||
|
packer packet.Packer,
|
||||||
|
) nnet.RunOption {
|
||||||
|
return func(ngin *nnet.Engine) {
|
||||||
|
ngin.EventManager().RegisterEventFront(event.EvtOnReceive, onServerReceiveEvent(handshakeResp, validator, packer))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func onServerReceiveEvent(
|
||||||
|
handshakeResp *HandshakeResp,
|
||||||
|
validator HandshakeValidatorFunc,
|
||||||
|
packer packet.Packer, ) event.OnReceiveFn {
|
||||||
|
return func(nc *conn.Connection, p packet.IPacket) {
|
||||||
|
pkg, ok := p.(*Packet)
|
||||||
|
if !ok {
|
||||||
|
nlog.Error(packet.ErrWrongPacketType)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch pkg.PacketType {
|
||||||
|
case Handshake:
|
||||||
|
var handshakeData HandshakeReq
|
||||||
|
err := json.Unmarshal(pkg.Data, &handshakeData)
|
||||||
|
nlog.Must(err)
|
||||||
|
|
||||||
|
if err := validator(&handshakeData); err != nil {
|
||||||
|
nlog.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
handshakeResp.Payload = handshakeData.Payload
|
||||||
|
|
||||||
|
data, err := json.Marshal(handshakeResp)
|
||||||
|
nlog.Must(err)
|
||||||
|
|
||||||
|
hrd, _ := packer.Pack(Header{
|
||||||
|
PacketType: Handshake,
|
||||||
|
MessageHeader: MessageHeader{},
|
||||||
|
}, data)
|
||||||
|
if err := nc.SendBytes(hrd); err != nil {
|
||||||
|
nlog.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nc.SetStatus(conn.StatusPrepare)
|
||||||
|
nlog.Debugf("connection handshake Id=%d, Remote=%s", nc.Session().ID(), nc.Conn().RemoteAddr())
|
||||||
|
case HandshakeAck:
|
||||||
|
nc.SetStatus(conn.StatusPending)
|
||||||
|
nlog.Debugf("receive handshake ACK Id=%d, Remote=%s", nc.Session().ID(), nc.Conn().RemoteAddr())
|
||||||
|
case Data:
|
||||||
|
if nc.Status() < conn.StatusPending {
|
||||||
|
nlog.Errorf("receive data on socket which not yet ACK, session will be closed immediately, remote=%s",
|
||||||
|
nc.Conn().RemoteAddr())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nc.SetStatus(conn.StatusWorking)
|
||||||
|
|
||||||
|
var lastMid uint64
|
||||||
|
switch pkg.MsgType {
|
||||||
|
case Request:
|
||||||
|
lastMid = pkg.ID
|
||||||
|
case Notify:
|
||||||
|
lastMid = 0
|
||||||
|
default:
|
||||||
|
nlog.Errorf("Invalid message type: %s ", pkg.MsgType.String())
|
||||||
|
}
|
||||||
|
nc.SetLastMID(lastMid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,78 +0,0 @@
|
|||||||
package nnet
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"git.noahlan.cn/noahlan/nnet"
|
|
||||||
"git.noahlan.cn/noahlan/nnet/connection"
|
|
||||||
"git.noahlan.cn/noahlan/nnet/packet"
|
|
||||||
"git.noahlan.cn/noahlan/ntool/nlog"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
HandshakeValidatorFunc func(*HandshakeReq) error
|
|
||||||
HandshakeAckPayloadFunc func() any
|
|
||||||
)
|
|
||||||
|
|
||||||
func withNNetPipeline(
|
|
||||||
handshakeResp *HandshakeResp,
|
|
||||||
validator HandshakeValidatorFunc,
|
|
||||||
packer packet.Packer,
|
|
||||||
) nnet.RunOption {
|
|
||||||
return func(ngin *nnet.Engine) {
|
|
||||||
ngin.Pipeline().Inbound().PushFront(func(conn *connection.Connection, v any) error {
|
|
||||||
pkg, ok := v.(*Packet)
|
|
||||||
if !ok {
|
|
||||||
return packet.ErrWrongPacketType
|
|
||||||
}
|
|
||||||
nc, _ := conn.Conn()
|
|
||||||
|
|
||||||
switch pkg.PacketType {
|
|
||||||
case Handshake:
|
|
||||||
var handshakeData HandshakeReq
|
|
||||||
err := json.Unmarshal(pkg.Data, &handshakeData)
|
|
||||||
nlog.Must(err)
|
|
||||||
|
|
||||||
if err := validator(&handshakeData); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
handshakeResp.Payload = handshakeData.Payload
|
|
||||||
|
|
||||||
data, err := json.Marshal(handshakeResp)
|
|
||||||
nlog.Must(err)
|
|
||||||
|
|
||||||
hrd, _ := packer.Pack(Header{
|
|
||||||
PacketType: Handshake,
|
|
||||||
MessageHeader: MessageHeader{},
|
|
||||||
}, data)
|
|
||||||
if err := conn.SendBytes(hrd); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
conn.SetStatus(connection.StatusPrepare)
|
|
||||||
nlog.Debugf("connection handshake Id=%d, Remote=%s", conn.Session().ID(), nc.RemoteAddr())
|
|
||||||
case HandshakeAck:
|
|
||||||
conn.SetStatus(connection.StatusPending)
|
|
||||||
nlog.Debugf("receive handshake ACK Id=%d, Remote=%s", conn.Session().ID(), nc.RemoteAddr())
|
|
||||||
case Data:
|
|
||||||
if conn.Status() < connection.StatusPending {
|
|
||||||
return errors.New(fmt.Sprintf("receive data on socket which not yet ACK, session will be closed immediately, remote=%s",
|
|
||||||
nc.RemoteAddr()))
|
|
||||||
}
|
|
||||||
conn.SetStatus(connection.StatusWorking)
|
|
||||||
|
|
||||||
var lastMid uint64
|
|
||||||
switch pkg.MsgType {
|
|
||||||
case Request:
|
|
||||||
lastMid = pkg.ID
|
|
||||||
case Notify:
|
|
||||||
lastMid = 0
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("Invalid message type: %s ", pkg.MsgType.String())
|
|
||||||
}
|
|
||||||
conn.SetLastMID(lastMid)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,22 @@
|
|||||||
|
package plain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.noahlan.cn/noahlan/nnet"
|
||||||
|
"git.noahlan.cn/noahlan/nnet/conn"
|
||||||
|
"git.noahlan.cn/noahlan/nnet/event"
|
||||||
|
"git.noahlan.cn/noahlan/nnet/packet"
|
||||||
|
)
|
||||||
|
|
||||||
|
func withEvents() nnet.RunOption {
|
||||||
|
return func(ngin *nnet.Engine) {
|
||||||
|
ngin.EventManager().RegisterEventFront(event.EvtOnReceive, onReceiveEvent())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func onReceiveEvent() event.OnReceiveFn {
|
||||||
|
return func(nc *conn.Connection, _ packet.IPacket) {
|
||||||
|
if nc.Status() != conn.StatusWorking {
|
||||||
|
nc.SetStatus(conn.StatusWorking)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,22 +0,0 @@
|
|||||||
package plain
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.noahlan.cn/noahlan/nnet"
|
|
||||||
"git.noahlan.cn/noahlan/nnet/connection"
|
|
||||||
"git.noahlan.cn/noahlan/nnet/packet"
|
|
||||||
)
|
|
||||||
|
|
||||||
func withPipeline() nnet.RunOption {
|
|
||||||
return func(ngin *nnet.Engine) {
|
|
||||||
ngin.Pipeline().Inbound().PushFront(func(conn *connection.Connection, v any) error {
|
|
||||||
_, ok := v.(*Packet)
|
|
||||||
if !ok {
|
|
||||||
return packet.ErrWrongPacketType
|
|
||||||
}
|
|
||||||
if conn.Status() != connection.StatusWorking {
|
|
||||||
conn.SetStatus(connection.StatusWorking)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,69 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.noahlan.cn/noahlan/nnet"
|
||||||
|
"git.noahlan.cn/noahlan/nnet/config"
|
||||||
|
"git.noahlan.cn/noahlan/nnet/conn"
|
||||||
|
"git.noahlan.cn/noahlan/nnet/packet"
|
||||||
|
"git.noahlan.cn/noahlan/nnet/protocol/plain"
|
||||||
|
rt "git.noahlan.cn/noahlan/nnet/router"
|
||||||
|
"git.noahlan.cn/noahlan/ntool/nlog"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func runWSServer(addr, path string) {
|
||||||
|
nginOpts := make([]nnet.RunOption, 0)
|
||||||
|
nginOpts = append(nginOpts, plain.WithPlainProtocol()...)
|
||||||
|
|
||||||
|
ngin := nnet.NewEngine(config.EngineConf{
|
||||||
|
Mode: config.DevMode,
|
||||||
|
Name: "DevNL",
|
||||||
|
}, nginOpts...)
|
||||||
|
|
||||||
|
ngin.AddRoutes(rt.Route{
|
||||||
|
Matches: nil,
|
||||||
|
Handler: func(conn *conn.Connection, pkg packet.IPacket) {
|
||||||
|
nlog.Debugf("route fn: %v", pkg)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
defer ngin.Stop()
|
||||||
|
|
||||||
|
err := ngin.ListenWebsocket(config.WSServerFullConf{
|
||||||
|
WSConf: config.WSConf{
|
||||||
|
Addr: addr,
|
||||||
|
Path: path,
|
||||||
|
HandshakeTimeout: time.Second * 5,
|
||||||
|
},
|
||||||
|
WSEvent: config.WSEvent{},
|
||||||
|
}, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runWSClient(addr string) (*nnet.Engine, *conn.Connection) {
|
||||||
|
//chReady := make(chan struct{})
|
||||||
|
|
||||||
|
nginOpts := make([]nnet.RunOption, 0)
|
||||||
|
nginOpts = append(nginOpts, plain.WithPlainProtocol()...)
|
||||||
|
|
||||||
|
//var onReadyFn event.OnConnectedFn = func(nc *conn.Connection) {
|
||||||
|
// chReady <- struct{}{}
|
||||||
|
//}
|
||||||
|
|
||||||
|
//nginOpts = append(nginOpts, func(ngin *nnet.Engine) {
|
||||||
|
// ngin.EventManager().RegisterEvent(event.EvtOnConnected, onReadyFn)
|
||||||
|
//})
|
||||||
|
|
||||||
|
ngin := nnet.NewEngine(config.EngineConf{
|
||||||
|
Mode: config.DevMode,
|
||||||
|
Name: "DevNL-Client",
|
||||||
|
}, nginOpts...)
|
||||||
|
|
||||||
|
nc, err := ngin.DialWebsocket(addr, config.WSClientFullConf{})
|
||||||
|
nlog.Must(err)
|
||||||
|
|
||||||
|
return ngin, nc
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWSServer(t *testing.T) {
|
||||||
|
runWSServer("0.0.0.0:14725", "/ws")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWSClient(t *testing.T) {
|
||||||
|
ngin, nc := runWSClient("ws://127.0.0.1:14725/ws")
|
||||||
|
|
||||||
|
_ = nc.Send(websocket.TextMessage, []byte("hello world!"))
|
||||||
|
|
||||||
|
ngin.LogPrefix()
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(1)
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
package nnet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.noahlan.cn/noahlan/nnet/config"
|
||||||
|
"git.noahlan.cn/noahlan/nnet/conn"
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WsEventOption func(conf config.WSEvent)
|
||||||
|
|
||||||
|
func WithPingHandler(fn func(appData string)) WsEventOption {
|
||||||
|
return func(conf config.WSEvent) {
|
||||||
|
conf.PingHandler = fn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithPongHandler(fn func(appData string)) WsEventOption {
|
||||||
|
return func(conf config.WSEvent) {
|
||||||
|
conf.PongHandler = fn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithCloseHandler(fn func(closeCode int, closeText string) error) WsEventOption {
|
||||||
|
return func(conf config.WSEvent) {
|
||||||
|
conf.CloseHandler = fn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ngin *Engine) handleWS(wc *websocket.Conn, conf config.WSEvent) *conn.Connection {
|
||||||
|
wsConn := conn.NewWSConn(wc)
|
||||||
|
|
||||||
|
nc := ngin.handle(wsConn)
|
||||||
|
|
||||||
|
defaultCloseHandler := wsConn.CloseHandler()
|
||||||
|
wsConn.SetCloseHandler(func(code int, text string) error {
|
||||||
|
result := defaultCloseHandler(code, text)
|
||||||
|
_ = wsConn.Close()
|
||||||
|
ngin.evtMgr.OnClose(nc)
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
|
||||||
|
// ping
|
||||||
|
defaultPingHandler := wsConn.PingHandler()
|
||||||
|
wsConn.SetPingHandler(func(appData string) error {
|
||||||
|
if conf.PingHandler != nil {
|
||||||
|
conf.PingHandler(appData)
|
||||||
|
}
|
||||||
|
return defaultPingHandler(appData)
|
||||||
|
})
|
||||||
|
|
||||||
|
// pong
|
||||||
|
defaultPongHandler := wsConn.PongHandler()
|
||||||
|
wsConn.SetPongHandler(func(appData string) error {
|
||||||
|
if conf.PongHandler != nil {
|
||||||
|
conf.PongHandler(appData)
|
||||||
|
}
|
||||||
|
return defaultPongHandler(appData)
|
||||||
|
})
|
||||||
|
|
||||||
|
return nc
|
||||||
|
}
|
Loading…
Reference in New Issue