parent
e9bb90ba8c
commit
d39b9921cf
@ -1,23 +1,23 @@
|
||||
package nnet
|
||||
|
||||
import (
|
||||
"git.noahlan.cn/noahlan/nnet/connection"
|
||||
"git.noahlan.cn/noahlan/nnet/conn"
|
||||
"git.noahlan.cn/noahlan/ntool/nlog"
|
||||
"net"
|
||||
)
|
||||
|
||||
// DialTCP 连接服务器
|
||||
func (ngin *Engine) DialTCP(addr string) (*connection.Connection, error) {
|
||||
func (ngin *Engine) DialTCP(addr string) (*conn.Connection, error) {
|
||||
err := ngin.setup()
|
||||
if err != nil {
|
||||
nlog.Errorf("%s failed to setup server, err:%v", ngin.LogPrefix(), err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn, err := net.Dial("tcp", addr)
|
||||
rc, err := net.Dial("tcp", addr)
|
||||
nlog.Must(err)
|
||||
|
||||
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
|
||||
|
||||
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 (
|
||||
"errors"
|
@ -1,4 +1,4 @@
|
||||
package connection
|
||||
package conn
|
||||
|
||||
import (
|
||||
"git.noahlan.cn/noahlan/ntool/nlog"
|
@ -1,4 +1,4 @@
|
||||
package connection
|
||||
package conn
|
||||
|
||||
import (
|
||||
"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 (
|
||||
"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