parent
d53b3f1d39
commit
ae8c4fc9d0
1
go.mod
1
go.mod
|
@ -14,6 +14,7 @@ require (
|
||||||
github.com/gobwas/pool v0.2.1 // indirect
|
github.com/gobwas/pool v0.2.1 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.9 // indirect
|
github.com/mattn/go-colorable v0.1.9 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||||
|
github.com/panjf2000/ants/v2 v2.7.1 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
|
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
|
||||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||||
go.opentelemetry.io/otel v1.11.0 // indirect
|
go.opentelemetry.io/otel v1.11.0 // indirect
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* 版权所有 (c) 上海元泓软件科技有限公司 2023.
|
||||||
|
* 严禁通过任何媒介未经授权复制本文件.
|
||||||
|
*
|
||||||
|
* 作者:mic
|
||||||
|
* Email:funui@outlook.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 版权所有 (c) 上海元泓软件科技有限公司 2022.
|
||||||
|
* 严禁通过任何媒介未经授权复制本文件.
|
||||||
|
*
|
||||||
|
* 作者:mic
|
||||||
|
* Email:funui@outlook.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
package tcp
|
||||||
|
|
||||||
|
type (
|
||||||
|
// A EchoConf is a socket API server config.
|
||||||
|
EchoConf struct {
|
||||||
|
// Valid network schemes:
|
||||||
|
// tcp - bind to both IPv4 and IPv6
|
||||||
|
// tcp4 - IPv4
|
||||||
|
// tcp6 - IPv6
|
||||||
|
// udp - bind to both IPv4 and IPv6
|
||||||
|
// udp4 - IPv4
|
||||||
|
// udp6 - IPv6
|
||||||
|
// unix - Unix Domain Socket
|
||||||
|
Network string
|
||||||
|
Host string `json:",default=0.0.0.0"`
|
||||||
|
Port int
|
||||||
|
CertFile string `json:",optional"`
|
||||||
|
KeyFile string `json:",optional"`
|
||||||
|
MaxConns int `json:",default=10000"`
|
||||||
|
|
||||||
|
// 协议包信息
|
||||||
|
Timeout int64 `json:",default=15000"`
|
||||||
|
Magic uint32 //包头
|
||||||
|
Key string //包数据校验Key
|
||||||
|
}
|
||||||
|
)
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* 版权所有 (c) 上海元泓软件科技有限公司 2023.
|
||||||
|
* 严禁通过任何媒介未经授权复制本文件.
|
||||||
|
*
|
||||||
|
* 作者:mic
|
||||||
|
* Email:funui@outlook.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 版权所有 (c) 上海元泓软件科技有限公司 2022.
|
||||||
|
* 严禁通过任何媒介未经授权复制本文件.
|
||||||
|
*
|
||||||
|
* 作者:mic
|
||||||
|
* Email:funui@outlook.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
package tcp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
goPool "github.com/panjf2000/gnet/v2/pkg/pool/goroutine"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
"yh-core/common/sapi/tcp/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
type engine struct {
|
||||||
|
conf EchoConf
|
||||||
|
routes featuredRoutes
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEngine(c EchoConf) *engine {
|
||||||
|
svr := &engine{
|
||||||
|
conf: c,
|
||||||
|
}
|
||||||
|
return svr
|
||||||
|
}
|
||||||
|
func (ng *engine) addRoutes(r featuredRoutes) {
|
||||||
|
ng.routes = r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ng *engine) start() error {
|
||||||
|
ss := &GnetServer{
|
||||||
|
workerPool: goPool.Default(),
|
||||||
|
network: ng.conf.Network,
|
||||||
|
addr: fmt.Sprintf("%s:%d", ng.conf.Host, ng.conf.Port),
|
||||||
|
}
|
||||||
|
ss.addRoutes(ng.routes)
|
||||||
|
|
||||||
|
return internal.StartEcho(ss, ng.conf.Network, ng.conf.Host, ng.conf.Port)
|
||||||
|
}
|
||||||
|
func (ng *engine) stop() error {
|
||||||
|
internal.StopEcho(ng.conf.Network, ng.conf.Host, ng.conf.Port)
|
||||||
|
return logx.Close()
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
* 版权所有 (c) 上海元泓软件科技有限公司 2023.
|
||||||
|
* 严禁通过任何媒介未经授权复制本文件.
|
||||||
|
*
|
||||||
|
* 作者:mic
|
||||||
|
* Email:funui@outlook.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 版权所有 (c) 上海元泓软件科技有限公司 2022.
|
||||||
|
* 严禁通过任何媒介未经授权复制本文件.
|
||||||
|
*
|
||||||
|
* 作者:mic
|
||||||
|
* Email:funui@outlook.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 版权所有 (c) 上海元泓软件科技有限公司 2022.
|
||||||
|
* 严禁通过任何媒介未经授权复制本文件.
|
||||||
|
*
|
||||||
|
* 作者:mic
|
||||||
|
* Email:funui@outlook.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
package tcp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/panjf2000/gnet/v2"
|
||||||
|
goPool "github.com/panjf2000/gnet/v2/pkg/pool/goroutine"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GnetServer struct {
|
||||||
|
gnet.BuiltinEventEngine
|
||||||
|
workerPool *goPool.Pool
|
||||||
|
//链接的对象汇总
|
||||||
|
//connectedSockets sync.Map
|
||||||
|
//心跳
|
||||||
|
tick time.Duration
|
||||||
|
|
||||||
|
eng gnet.Engine
|
||||||
|
|
||||||
|
network string
|
||||||
|
addr string
|
||||||
|
|
||||||
|
connected int32
|
||||||
|
maxConnected int32
|
||||||
|
|
||||||
|
handlers sync.Map
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GnetServer) addRoutes(r featuredRoutes) {
|
||||||
|
for _, route := range r.routes {
|
||||||
|
s.handlers.Store(route.Command, route.Handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GnetServer) OnBoot(eng gnet.Engine) (action gnet.Action) {
|
||||||
|
logx.Infof("服务启动成功 %s ",
|
||||||
|
fmt.Sprintf("%s://%s", s.network, s.addr))
|
||||||
|
s.eng = eng
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GnetServer) OnShutdown(eng gnet.Engine) {
|
||||||
|
//关闭工作池
|
||||||
|
logx.Infof("关闭服务 %s ",
|
||||||
|
fmt.Sprintf("%s://%s", s.network, s.addr))
|
||||||
|
s.workerPool.Release()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GnetServer) OnOpen(c gnet.Conn) (out []byte, action gnet.Action) {
|
||||||
|
|
||||||
|
connected := atomic.AddInt32(&s.connected, 1)
|
||||||
|
maxConnected := atomic.LoadInt32(&s.maxConnected)
|
||||||
|
if connected > maxConnected {
|
||||||
|
atomic.SwapInt32(&s.maxConnected, connected)
|
||||||
|
}
|
||||||
|
logx.Infof("在线:%d,最高在线:%d", connected, atomic.LoadInt32(&s.maxConnected))
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GnetServer) OnClose(c gnet.Conn, err error) (action gnet.Action) {
|
||||||
|
if err != nil {
|
||||||
|
logx.Infof("error occurred on connection=%s, %v\n", c.RemoteAddr().String(), err)
|
||||||
|
}
|
||||||
|
connected := atomic.AddInt32(&s.connected, -1)
|
||||||
|
logx.Infof("在线:%d,最高在线:%d", connected, atomic.LoadInt32(&s.maxConnected))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GnetServer) OnTraffic(c gnet.Conn) (action gnet.Action) {
|
||||||
|
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
pkg YHProto.EchoPackage
|
||||||
|
)
|
||||||
|
|
||||||
|
for {
|
||||||
|
err = pkg.DecodeGnet(c)
|
||||||
|
if err == YHProto.ErrNotEnoughStream {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
logx.Errorf("invalid packet: %v", err)
|
||||||
|
return gnet.Close
|
||||||
|
}
|
||||||
|
|
||||||
|
handler, ok := s.handlers.Load(pkg.H.Command)
|
||||||
|
if !ok {
|
||||||
|
logx.Errorf("illegal command{%d}", pkg.H.Command)
|
||||||
|
return gnet.Close
|
||||||
|
}
|
||||||
|
//处理pkg
|
||||||
|
_ = s.workerPool.Submit(
|
||||||
|
func() {
|
||||||
|
h := handler.(HandlerFunc)
|
||||||
|
h(c, &pkg)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GnetServer) OnTick() (delay time.Duration, action gnet.Action) {
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* 版权所有 (c) 上海元泓软件科技有限公司 2022.
|
||||||
|
* 严禁通过任何媒介未经授权复制本文件.
|
||||||
|
*
|
||||||
|
* 作者:mic
|
||||||
|
* Email:funui@outlook.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/panjf2000/gnet/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func StartEcho(eventHandler gnet.EventHandler, network string, host string, port int) error {
|
||||||
|
|
||||||
|
if network == "ws" {
|
||||||
|
return gnet.Run(eventHandler, fmt.Sprintf("tcp://%s:%d", host, port))
|
||||||
|
}
|
||||||
|
return gnet.Run(eventHandler, fmt.Sprintf("%s://%s:%d", network, host, port))
|
||||||
|
}
|
||||||
|
func StopEcho(network string, host string, port int) {
|
||||||
|
gnet.Stop(context.Background(), fmt.Sprintf("%s://%s:%d", network, host, port))
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* 版权所有 (c) 上海元泓软件科技有限公司 2023.
|
||||||
|
* 严禁通过任何媒介未经授权复制本文件.
|
||||||
|
*
|
||||||
|
* 作者:mic
|
||||||
|
* Email:funui@outlook.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 版权所有 (c) 上海元泓软件科技有限公司 2022.
|
||||||
|
* 严禁通过任何媒介未经授权复制本文件.
|
||||||
|
*
|
||||||
|
* 作者:mic
|
||||||
|
* Email:funui@outlook.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
package tcp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A EchoServer is a rpc server.
|
||||||
|
type Server struct {
|
||||||
|
echo *engine
|
||||||
|
}
|
||||||
|
|
||||||
|
func MustNewServer(c EchoConf) *Server {
|
||||||
|
//设置协议信息
|
||||||
|
YHProto.SetEchoPkgMagic(c.Magic)
|
||||||
|
YHProto.SetEchoPkgKey(c.Key)
|
||||||
|
|
||||||
|
server, err := NewServer(c)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
return server
|
||||||
|
}
|
||||||
|
func NewServer(c EchoConf) (*Server, error) {
|
||||||
|
server := &Server{
|
||||||
|
echo: newEngine(c),
|
||||||
|
}
|
||||||
|
return server, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRoutes add given routes into the Server.
|
||||||
|
func (s *Server) AddRoutes(rs []Route) {
|
||||||
|
r := featuredRoutes{
|
||||||
|
routes: rs,
|
||||||
|
}
|
||||||
|
s.echo.addRoutes(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRoute adds given route into the Server.
|
||||||
|
func (s *Server) AddRoute(r Route) {
|
||||||
|
s.AddRoutes([]Route{r})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Start() {
|
||||||
|
err := s.echo.start()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (s *Server) Stop() {
|
||||||
|
err := s.echo.stop()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* 版权所有 (c) 上海元泓软件科技有限公司 2023.
|
||||||
|
* 严禁通过任何媒介未经授权复制本文件.
|
||||||
|
*
|
||||||
|
* 作者:mic
|
||||||
|
* Email:funui@outlook.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 版权所有 (c) 上海元泓软件科技有限公司 2022.
|
||||||
|
* 严禁通过任何媒介未经授权复制本文件.
|
||||||
|
*
|
||||||
|
* 作者:mic
|
||||||
|
* Email:funui@outlook.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
package tcp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/panjf2000/gnet/v2"
|
||||||
|
"yh-core/common/sapi/tcp/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
HandlerFunc func(gnet.Conn, *protocol.EchoPackage)
|
||||||
|
//PackageHandler interface {
|
||||||
|
// Handle(gnet.Conn, *protocol.EchoPackage) error
|
||||||
|
//}
|
||||||
|
// A handlers is a socket cmd.
|
||||||
|
Route struct {
|
||||||
|
Command uint16 //operation command code
|
||||||
|
Handler HandlerFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
featuredRoutes struct {
|
||||||
|
routes []Route
|
||||||
|
}
|
||||||
|
)
|
|
@ -0,0 +1,169 @@
|
||||||
|
/*
|
||||||
|
* 版权所有 (c) 上海元泓软件科技有限公司 2023.
|
||||||
|
* 严禁通过任何媒介未经授权复制本文件.
|
||||||
|
*
|
||||||
|
* 作者:mic
|
||||||
|
* Email:funui@outlook.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
package wsgnet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"github.com/gobwas/ws"
|
||||||
|
"github.com/gobwas/ws/wsutil"
|
||||||
|
"github.com/panjf2000/gnet/v2"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type wsCodec struct {
|
||||||
|
upgraded bool // 链接是否升级
|
||||||
|
buf bytes.Buffer // 从实际socket中读取到的数据缓存
|
||||||
|
wsMsgBuf wsMessageBuf // ws 消息缓存
|
||||||
|
}
|
||||||
|
|
||||||
|
type wsMessageBuf struct {
|
||||||
|
firstHeader *ws.Header
|
||||||
|
curHeader *ws.Header
|
||||||
|
cachedBuf bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
type readWrite struct {
|
||||||
|
io.Reader
|
||||||
|
io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wsCodec) upgrade(c gnet.Conn) (ok bool, action gnet.Action) {
|
||||||
|
if w.upgraded {
|
||||||
|
ok = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
buf := &w.buf
|
||||||
|
tmpReader := bytes.NewReader(buf.Bytes())
|
||||||
|
oldLen := tmpReader.Len()
|
||||||
|
logx.Infof("do Upgrade")
|
||||||
|
|
||||||
|
hs, err := ws.Upgrade(readWrite{tmpReader, c})
|
||||||
|
skipN := oldLen - tmpReader.Len()
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF || err == io.ErrUnexpectedEOF { //数据不完整
|
||||||
|
return
|
||||||
|
}
|
||||||
|
buf.Next(skipN)
|
||||||
|
logx.Infof("conn[%v] [err=%v]", c.RemoteAddr().String(), err.Error())
|
||||||
|
action = gnet.Close
|
||||||
|
return
|
||||||
|
}
|
||||||
|
buf.Next(skipN)
|
||||||
|
logx.Infof("conn[%v] upgrade websocket protocol! Handshake: %v", c.RemoteAddr().String(), hs)
|
||||||
|
if err != nil {
|
||||||
|
logx.Infof("conn[%v] [err=%v]", c.RemoteAddr().String(), err.Error())
|
||||||
|
action = gnet.Close
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ok = true
|
||||||
|
w.upgraded = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (w *wsCodec) readBufferBytes(c gnet.Conn) gnet.Action {
|
||||||
|
size := c.InboundBuffered()
|
||||||
|
buf := make([]byte, size, size)
|
||||||
|
read, err := c.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
logx.Infof("read err! %w", err)
|
||||||
|
return gnet.Close
|
||||||
|
}
|
||||||
|
if read < size {
|
||||||
|
logx.Infof("read bytes len err! size: %d read: %d", size, read)
|
||||||
|
return gnet.Close
|
||||||
|
}
|
||||||
|
w.buf.Write(buf)
|
||||||
|
return gnet.None
|
||||||
|
}
|
||||||
|
func (w *wsCodec) Decode(c gnet.Conn) (outs []wsutil.Message, err error) {
|
||||||
|
fmt.Println("do Decode")
|
||||||
|
messages, err := w.readWsMessages()
|
||||||
|
if err != nil {
|
||||||
|
logx.Infof("Error reading message! %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if messages == nil || len(messages) <= 0 { //没有读到完整数据 不处理
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, message := range messages {
|
||||||
|
if message.OpCode.IsControl() {
|
||||||
|
err = wsutil.HandleClientControlMessage(c, message)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if message.OpCode == ws.OpText || message.OpCode == ws.OpBinary {
|
||||||
|
outs = append(outs, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wsCodec) readWsMessages() (messages []wsutil.Message, err error) {
|
||||||
|
msgBuf := &w.wsMsgBuf
|
||||||
|
in := &w.buf
|
||||||
|
for {
|
||||||
|
if msgBuf.curHeader == nil {
|
||||||
|
if in.Len() < ws.MinHeaderSize { //头长度至少是2
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var head ws.Header
|
||||||
|
if in.Len() >= ws.MaxHeaderSize {
|
||||||
|
head, err = ws.ReadHeader(in)
|
||||||
|
if err != nil {
|
||||||
|
return messages, err
|
||||||
|
}
|
||||||
|
} else { //有可能不完整,构建新的 reader 读取 head 读取成功才实际对 in 进行读操作
|
||||||
|
tmpReader := bytes.NewReader(in.Bytes())
|
||||||
|
oldLen := tmpReader.Len()
|
||||||
|
head, err = ws.ReadHeader(tmpReader)
|
||||||
|
skipN := oldLen - tmpReader.Len()
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF || err == io.ErrUnexpectedEOF { //数据不完整
|
||||||
|
return messages, nil
|
||||||
|
}
|
||||||
|
in.Next(skipN)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
in.Next(skipN)
|
||||||
|
}
|
||||||
|
|
||||||
|
msgBuf.curHeader = &head
|
||||||
|
err = ws.WriteHeader(&msgBuf.cachedBuf, head)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dataLen := (int)(msgBuf.curHeader.Length)
|
||||||
|
if dataLen > 0 {
|
||||||
|
if in.Len() >= dataLen {
|
||||||
|
_, err = io.CopyN(&msgBuf.cachedBuf, in, int64(dataLen))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else { //数据不完整
|
||||||
|
fmt.Println(in.Len(), dataLen)
|
||||||
|
logx.Infof("incomplete data")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if msgBuf.curHeader.Fin { //当前 header 已经是一个完整消息
|
||||||
|
messages, err = wsutil.ReadClientMessage(&msgBuf.cachedBuf, messages)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
msgBuf.cachedBuf.Reset()
|
||||||
|
} else {
|
||||||
|
logx.Infof("The data is split into multiple frames")
|
||||||
|
}
|
||||||
|
msgBuf.curHeader = nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* 版权所有 (c) 上海元泓软件科技有限公司 2023.
|
||||||
|
* 严禁通过任何媒介未经授权复制本文件.
|
||||||
|
*
|
||||||
|
* 作者:mic
|
||||||
|
* Email:funui@outlook.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 版权所有 (c) 上海元泓软件科技有限公司 2022.
|
||||||
|
* 严禁通过任何媒介未经授权复制本文件.
|
||||||
|
*
|
||||||
|
* 作者:mic
|
||||||
|
* Email:funui@outlook.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
package wsgnet
|
||||||
|
|
||||||
|
type (
|
||||||
|
// A EchoConf is a socket API server config.
|
||||||
|
EchoConf struct {
|
||||||
|
// Valid network schemes:
|
||||||
|
// tcp - bind to both IPv4 and IPv6
|
||||||
|
// tcp4 - IPv4
|
||||||
|
// tcp6 - IPv6
|
||||||
|
// udp - bind to both IPv4 and IPv6
|
||||||
|
// udp4 - IPv4
|
||||||
|
// udp6 - IPv6
|
||||||
|
// unix - Unix Domain Socket
|
||||||
|
Network string
|
||||||
|
Host string `json:",default=0.0.0.0"`
|
||||||
|
Port int
|
||||||
|
CertFile string `json:",optional"`
|
||||||
|
KeyFile string `json:",optional"`
|
||||||
|
MaxConns int `json:",default=10000"`
|
||||||
|
|
||||||
|
// 协议包信息
|
||||||
|
Timeout int64 `json:",default=15000"`
|
||||||
|
Magic uint32 //包头
|
||||||
|
Key string //包数据校验Key
|
||||||
|
}
|
||||||
|
)
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* 版权所有 (c) 上海元泓软件科技有限公司 2022.
|
||||||
|
* 严禁通过任何媒介未经授权复制本文件.
|
||||||
|
*
|
||||||
|
* 作者:mic
|
||||||
|
* Email:funui@outlook.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
package wsgnet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
goPool "github.com/panjf2000/gnet/v2/pkg/pool/goroutine"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
"yh-core/common/sapi/ws/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
type engine struct {
|
||||||
|
conf EchoConf
|
||||||
|
routes featuredRoutes
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEngine(c EchoConf) *engine {
|
||||||
|
svr := &engine{
|
||||||
|
conf: c,
|
||||||
|
}
|
||||||
|
return svr
|
||||||
|
}
|
||||||
|
func (ng *engine) addRoutes(r featuredRoutes) {
|
||||||
|
ng.routes = r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ng *engine) start() error {
|
||||||
|
ss := &GnetServer{
|
||||||
|
workerPool: goPool.Default(),
|
||||||
|
network: ng.conf.Network,
|
||||||
|
addr: fmt.Sprintf("%s:%d", ng.conf.Host, ng.conf.Port),
|
||||||
|
}
|
||||||
|
ss.addRoutes(ng.routes)
|
||||||
|
|
||||||
|
return internal.StartEcho(ss, ng.conf.Network, ng.conf.Host, ng.conf.Port)
|
||||||
|
}
|
||||||
|
func (ng *engine) stop() error {
|
||||||
|
internal.StopEcho(ng.conf.Network, ng.conf.Host, ng.conf.Port)
|
||||||
|
return logx.Close()
|
||||||
|
}
|
|
@ -0,0 +1,154 @@
|
||||||
|
/*
|
||||||
|
* 版权所有 (c) 上海元泓软件科技有限公司 2022.
|
||||||
|
* 严禁通过任何媒介未经授权复制本文件.
|
||||||
|
*
|
||||||
|
* 作者:mic
|
||||||
|
* Email:funui@outlook.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
package wsgnet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gobwas/ws"
|
||||||
|
"github.com/gobwas/ws/wsutil"
|
||||||
|
"github.com/panjf2000/gnet/v2"
|
||||||
|
goPool "github.com/panjf2000/gnet/v2/pkg/pool/goroutine"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GnetServer struct {
|
||||||
|
gnet.BuiltinEventEngine
|
||||||
|
workerPool *goPool.Pool
|
||||||
|
//链接的对象汇总
|
||||||
|
//connectedSockets sync.Map
|
||||||
|
//心跳
|
||||||
|
tick time.Duration
|
||||||
|
|
||||||
|
eng gnet.Engine
|
||||||
|
|
||||||
|
network string
|
||||||
|
addr string
|
||||||
|
|
||||||
|
connected int32
|
||||||
|
maxConnected int32
|
||||||
|
|
||||||
|
handlers sync.Map
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GnetServer) addRoutes(r featuredRoutes) {
|
||||||
|
for _, route := range r.routes {
|
||||||
|
s.handlers.Store(route.Command, route.Handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GnetServer) OnBoot(eng gnet.Engine) (action gnet.Action) {
|
||||||
|
logx.Infof("服务启动成功 %s ",
|
||||||
|
fmt.Sprintf("%s://%s", s.network, s.addr))
|
||||||
|
s.eng = eng
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GnetServer) OnShutdown(eng gnet.Engine) {
|
||||||
|
//关闭工作池
|
||||||
|
logx.Infof("关闭服务 %s ",
|
||||||
|
fmt.Sprintf("%s://%s", s.network, s.addr))
|
||||||
|
s.workerPool.Release()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GnetServer) OnOpen(c gnet.Conn) (out []byte, action gnet.Action) {
|
||||||
|
c.SetContext(new(wsCodec))
|
||||||
|
connected := atomic.AddInt32(&s.connected, 1)
|
||||||
|
maxConnected := atomic.LoadInt32(&s.maxConnected)
|
||||||
|
if connected > maxConnected {
|
||||||
|
atomic.SwapInt32(&s.maxConnected, connected)
|
||||||
|
}
|
||||||
|
logx.Infof("在线:%d,最高在线:%d", connected, atomic.LoadInt32(&s.maxConnected))
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GnetServer) OnClose(c gnet.Conn, err error) (action gnet.Action) {
|
||||||
|
if err != nil {
|
||||||
|
logx.Infof("error occurred on connection=%s, %v\n", c.RemoteAddr().String(), err)
|
||||||
|
}
|
||||||
|
connected := atomic.AddInt32(&s.connected, -1)
|
||||||
|
logx.Infof("在线:%d,最高在线:%d", connected, atomic.LoadInt32(&s.maxConnected))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GnetServer) OnTraffic(c gnet.Conn) (action gnet.Action) {
|
||||||
|
wsc := c.Context().(*wsCodec)
|
||||||
|
if wsc.readBufferBytes(c) == gnet.Close {
|
||||||
|
return gnet.Close
|
||||||
|
}
|
||||||
|
ok, action := wsc.upgrade(c)
|
||||||
|
if !ok {
|
||||||
|
return action
|
||||||
|
}
|
||||||
|
|
||||||
|
if wsc.buf.Len() <= 0 {
|
||||||
|
return gnet.None
|
||||||
|
}
|
||||||
|
messages, err := wsc.Decode(c)
|
||||||
|
if err != nil {
|
||||||
|
return gnet.Close
|
||||||
|
}
|
||||||
|
|
||||||
|
if messages == nil {
|
||||||
|
return gnet.None
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
pkg YHProto.EchoPackage
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, message := range messages {
|
||||||
|
switch message.OpCode {
|
||||||
|
case ws.OpContinuation:
|
||||||
|
case ws.OpText:
|
||||||
|
logx.Infof("conn[%v] receive [op=%v] [msg=%v, len=%d]", c.RemoteAddr().String(), message.OpCode, string(message.Payload), len(message.Payload))
|
||||||
|
err = wsutil.WriteServerMessage(c, ws.OpText, message.Payload)
|
||||||
|
if err != nil {
|
||||||
|
logx.Infof("conn[%v] [err=%v]", c.RemoteAddr().String(), err.Error())
|
||||||
|
return gnet.Close
|
||||||
|
}
|
||||||
|
case ws.OpBinary:
|
||||||
|
err = pkg.DecodeWsMessage(message)
|
||||||
|
if err == YHProto.ErrNotEnoughStream {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
logx.Errorf("invalid packet: %v", err)
|
||||||
|
return gnet.Close
|
||||||
|
}
|
||||||
|
|
||||||
|
handler, ok := s.handlers.Load(pkg.H.Command)
|
||||||
|
if !ok {
|
||||||
|
logx.Errorf("illegal command{%d}", pkg.H.Command)
|
||||||
|
return gnet.Close
|
||||||
|
}
|
||||||
|
//处理pkg
|
||||||
|
_ = s.workerPool.Submit(
|
||||||
|
func() {
|
||||||
|
h := handler.(HandlerFunc)
|
||||||
|
h(c, &pkg)
|
||||||
|
})
|
||||||
|
case ws.OpClose:
|
||||||
|
return gnet.Close
|
||||||
|
case ws.OpPing:
|
||||||
|
case ws.OpPong:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GnetServer) OnTick() (delay time.Duration, action gnet.Action) {
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* 版权所有 (c) 上海元泓软件科技有限公司 2023.
|
||||||
|
* 严禁通过任何媒介未经授权复制本文件.
|
||||||
|
*
|
||||||
|
* 作者:mic
|
||||||
|
* Email:funui@outlook.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 版权所有 (c) 上海元泓软件科技有限公司 2023.
|
||||||
|
* 严禁通过任何媒介未经授权复制本文件.
|
||||||
|
*
|
||||||
|
* 作者:mic
|
||||||
|
* Email:funui@outlook.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
package wsgnet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gobwas/ws"
|
||||||
|
"github.com/gobwas/ws/wsutil"
|
||||||
|
"github.com/panjf2000/gnet/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func WriteServerMessage(c gnet.Conn, op ws.OpCode, p []byte) error {
|
||||||
|
if c.Context() != nil {
|
||||||
|
err := wsutil.WriteServerMessage(c, op, p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_, err := c.Write(p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* 版权所有 (c) 上海元泓软件科技有限公司 2022.
|
||||||
|
* 严禁通过任何媒介未经授权复制本文件.
|
||||||
|
*
|
||||||
|
* 作者:mic
|
||||||
|
* Email:funui@outlook.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/panjf2000/gnet/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func StartEcho(eventHandler gnet.EventHandler, network string, host string, port int) error {
|
||||||
|
|
||||||
|
if network == "ws" {
|
||||||
|
return gnet.Run(eventHandler, fmt.Sprintf("tcp://%s:%d", host, port), gnet.WithReusePort(true))
|
||||||
|
}
|
||||||
|
return errors.New(" only network ws!")
|
||||||
|
|
||||||
|
}
|
||||||
|
func StopEcho(network string, host string, port int) {
|
||||||
|
gnet.Stop(context.Background(), fmt.Sprintf("%s://%s:%d", network, host, port))
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* 版权所有 (c) 上海元泓软件科技有限公司 2023.
|
||||||
|
* 严禁通过任何媒介未经授权复制本文件.
|
||||||
|
*
|
||||||
|
* 作者:mic
|
||||||
|
* Email:funui@outlook.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 版权所有 (c) 上海元泓软件科技有限公司 2022.
|
||||||
|
* 严禁通过任何媒介未经授权复制本文件.
|
||||||
|
*
|
||||||
|
* 作者:mic
|
||||||
|
* Email:funui@outlook.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
package wsgnet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.yhrjkj.com/YuanHong/YHEchoPackage/YHProto"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A EchoServer is a rpc server.
|
||||||
|
type Server struct {
|
||||||
|
echo *engine
|
||||||
|
}
|
||||||
|
|
||||||
|
func MustNewServer(c EchoConf) *Server {
|
||||||
|
//设置协议信息
|
||||||
|
YHProto.SetEchoPkgMagic(c.Magic)
|
||||||
|
YHProto.SetEchoPkgKey(c.Key)
|
||||||
|
|
||||||
|
server, err := NewServer(c)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
return server
|
||||||
|
}
|
||||||
|
func NewServer(c EchoConf) (*Server, error) {
|
||||||
|
server := &Server{
|
||||||
|
echo: newEngine(c),
|
||||||
|
}
|
||||||
|
return server, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRoutes add given routes into the Server.
|
||||||
|
func (s *Server) AddRoutes(rs []Route) {
|
||||||
|
r := featuredRoutes{
|
||||||
|
routes: rs,
|
||||||
|
}
|
||||||
|
s.echo.addRoutes(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRoute adds given route into the Server.
|
||||||
|
func (s *Server) AddRoute(r Route) {
|
||||||
|
s.AddRoutes([]Route{r})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Start() {
|
||||||
|
err := s.echo.start()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (s *Server) Stop() {
|
||||||
|
err := s.echo.stop()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* 版权所有 (c) 上海元泓软件科技有限公司 2023.
|
||||||
|
* 严禁通过任何媒介未经授权复制本文件.
|
||||||
|
*
|
||||||
|
* 作者:mic
|
||||||
|
* Email:funui@outlook.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 版权所有 (c) 上海元泓软件科技有限公司 2022.
|
||||||
|
* 严禁通过任何媒介未经授权复制本文件.
|
||||||
|
*
|
||||||
|
* 作者:mic
|
||||||
|
* Email:funui@outlook.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
package wsgnet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/panjf2000/gnet/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
HandlerFunc func(gnet.Conn, *YHProto.EchoPackage)
|
||||||
|
//PackageHandler interface {
|
||||||
|
// Handle(gnet.Conn, *protocol.EchoPackage) error
|
||||||
|
//}
|
||||||
|
// A handlers is a socket cmd.
|
||||||
|
Route struct {
|
||||||
|
Command uint16 //operation command code
|
||||||
|
Handler HandlerFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
featuredRoutes struct {
|
||||||
|
routes []Route
|
||||||
|
}
|
||||||
|
)
|
Loading…
Reference in New Issue