长连接接口增加玩家信息绑定

This commit is contained in:
ayflying
2025-04-10 15:42:08 +08:00
parent 366ddb45ea
commit 08f2b2d9bc
5 changed files with 205 additions and 30 deletions

View File

@@ -26,7 +26,8 @@ var (
}, },
Examples: "make -m act -i 1: 创建活动1的接口与服务文件 \n" + Examples: "make -m act -i 1: 创建活动1的接口与服务文件 \n" +
"make -m logic -n test: 创建test的服务文件 \n" + "make -m logic -n test: 创建test的服务文件 \n" +
"make -m config -n test: 创建配置文件", "make -m config -n test: 创建配置文件 \n" +
"make -m socket -n test: 创建socket文件 \n",
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) { Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
//g.Dump(parser.GetOptAll(), parser.GetArgAll()) //g.Dump(parser.GetOptAll(), parser.GetArgAll())
@@ -53,6 +54,12 @@ var (
return return
} }
err = this.Config(name) err = this.Config(name)
case "socket":
var name = parser.GetOpt("name").String()
if name == "" {
return
}
err = this.Socket(name)
} }
return return
@@ -116,3 +123,25 @@ func (c *cMake) Config(name string) (err error) {
return return
} }
func (c *cMake) Socket(name string) (err error) {
var filePath = fmt.Sprintf("internal/socket/%s/%s_new.go", name, gstr.CaseSnake(name))
//生成文件不覆盖
if !gfile.Exists(filePath) {
// 生成目录文件
get, _ := fs.ReadFile(ConfigFiles, "make/socket")
fileStr := string(get)
fileStr = gstr.Replace(fileStr, "{name}", name)
err = gfile.PutContents(filePath, fileStr)
//生成方法文件
var filePath2 = fmt.Sprintf("internal/socket/%s/%s.go", name, gstr.CaseSnake(name))
get, _ = fs.ReadFile(ConfigFiles, "make/socket2")
fileStr = string(get)
fileStr = gstr.Replace(fileStr, "{name}", name)
fileStr = gstr.Replace(fileStr, "{func}", gstr.CaseCamel(name))
err = gfile.PutContents(filePath2, fileStr)
}
return
}

10
cmd/make/socket Normal file
View File

@@ -0,0 +1,10 @@
package {name}
type {name} struct {
}
func New() *{name} {
return &{name}{}
}
func init() {}

19
cmd/make/socket2 Normal file
View File

@@ -0,0 +1,19 @@
package {name}
import (
"github.com/ayflying/utility_go/pkg"
"github.com/ayflying/utility_go/pkg/websocket"
"google.golang.org/protobuf/proto"
)
func (s *{name}) {func}Handler(conn *websocket.WebsocketData, req any) (err error) {
var data = &v1.{func}2S{}
err = proto.Unmarshal(req.([]byte), data)
var res = &v1.{func}2C{}
resp, err := proto.Marshal(res)
pkg.Websocket().Send(000000, conn.Uid, resp)
return
}

View File

@@ -1,14 +1,24 @@
package websocket package websocket
import "google.golang.org/protobuf/proto"
// 定义一个处理方法的类型 // 定义一个处理方法的类型
type Handler func(conn *WebsocketData, req any) (err error) type Handler func(conn *WebsocketData, req any) (err error)
type OnConnectHandler func(conn *WebsocketData) type Handler2 func(conn *WebsocketData)
type HandlerMessage func(conn *WebsocketData, req any)
type PbType func(code int32, data []byte) proto.Message
type PbType2 func(data []byte) (int, []byte)
// 路由器的处理映射 // 路由器的处理映射
var ( var (
handlers = make(map[int]Handler) handlers = make(map[int]Handler)
OnConnectHandlers = make([]OnConnectHandler, 0) OnConnectHandlers = make([]Handler2, 0)
OnCloseHandlers = make([]OnConnectHandler, 0) OnCloseHandlers = make([]Handler2, 0)
onMessageHandlers = make([]HandlerMessage, 0)
Byte2Pb = make([]PbType, 0)
Pb2Bytes = make([]PbType2, 0)
) )
// 注册方法将某个消息路由器ID和对应的处理方法关联起来 // 注册方法将某个消息路由器ID和对应的处理方法关联起来
@@ -17,10 +27,23 @@ func (s *SocketV1) RegisterRouter(cmd int, handler Handler) {
} }
//注册方法,讲长连接登陆方法进行注册 //注册方法,讲长连接登陆方法进行注册
func (s *SocketV1) RegisterOnConnect(_func OnConnectHandler) { func (s *SocketV1) RegisterOnConnect(_func Handler2) {
OnConnectHandlers = append(OnConnectHandlers, _func) OnConnectHandlers = append(OnConnectHandlers, _func)
} }
func (s *SocketV1) RegisterOnClose(_func OnConnectHandler) { func (s *SocketV1) RegisterOnClose(_func Handler2) {
OnCloseHandlers = append(OnCloseHandlers, _func) OnCloseHandlers = append(OnCloseHandlers, _func)
} }
//注册方法长连接消息体
func (s *SocketV1) RegisterMessage(_func HandlerMessage) {
onMessageHandlers = append(onMessageHandlers, _func)
}
func (s *SocketV1) RegisterByte2Pb(_func PbType) {
Byte2Pb = append(Byte2Pb, _func)
}
func (s *SocketV1) RegisterPb2Byte(_func PbType2) {
Pb2Bytes = append(Pb2Bytes, _func)
}

View File

@@ -2,13 +2,15 @@ package websocket
import ( import (
"context" "context"
"fmt"
"github.com/gogf/gf/v2/container/gmap" "github.com/gogf/gf/v2/container/gmap"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/glog" "github.com/gogf/gf/v2/os/glog"
"github.com/gogf/gf/v2/util/gconv" "github.com/gogf/gf/v2/util/guid"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"google.golang.org/protobuf/proto"
"strconv" "strconv"
"sync" "sync"
) )
@@ -22,19 +24,19 @@ var (
//Conn map[uuid.UUID]*WebsocketData //Conn map[uuid.UUID]*WebsocketData
lock sync.Mutex lock sync.Mutex
m = gmap.New(true) m = gmap.NewHashMap(true)
) )
type WebsocketData struct { type WebsocketData struct {
Ws *websocket.Conn Ws *websocket.Conn
Uuid uuid.UUID Uuid string
Uid int64 Uid int64
Ctx context.Context Ctx context.Context
} }
func NewV1() *SocketV1 { func NewV1() *SocketV1 {
return &SocketV1{ return &SocketV1{
Type: 1, Type: 2,
} }
} }
@@ -77,11 +79,9 @@ func (s *SocketV1) Load(serv *ghttp.Server, prefix string) {
// @receiver s // @receiver s
// @param conn // @param conn
func (s *SocketV1) OnConnect(ctx context.Context, conn *websocket.Conn) { func (s *SocketV1) OnConnect(ctx context.Context, conn *websocket.Conn) {
//lock.Lock()
//defer lock.Unlock()
defer conn.Close() defer conn.Close()
id, _ := uuid.NewUUID() id := guid.S()
ip := conn.RemoteAddr().String() ip := conn.RemoteAddr().String()
data := &WebsocketData{ data := &WebsocketData{
Uuid: id, Uuid: id,
@@ -103,8 +103,10 @@ func (s *SocketV1) OnConnect(ctx context.Context, conn *websocket.Conn) {
for { for {
//进入当前连接线程拥堵 //进入当前连接线程拥堵
msgType, msg, err := conn.ReadMessage() msgType, msg, err := conn.ReadMessage()
s.Type = msgType
if err != nil { if err != nil {
//客户端断开返回错误,断开当前连接 //客户端断开返回错误,断开当前连接
//g.Log().Error(ctx, err)
break break
} }
s.OnMessage(m.Get(id).(*WebsocketData), msg, msgType) s.OnMessage(m.Get(id).(*WebsocketData), msg, msgType)
@@ -121,53 +123,142 @@ func (s *SocketV1) OnConnect(ctx context.Context, conn *websocket.Conn) {
// @param msg // @param msg
// @param msgType // @param msgType
func (s *SocketV1) OnMessage(conn *WebsocketData, req []byte, msgType int) { func (s *SocketV1) OnMessage(conn *WebsocketData, req []byte, msgType int) {
s.Type = msgType s.Type = 2
//g.Log().Debugf(ctx, "收到消息:%v,type=%v,conn=%v", string(req), msgType, conn)
//s.Send(conn.Uuid, msg) var cmd int
//s.SendAll(msg) var msg []byte
//uid := conn.Uid
for _, v := range Pb2Bytes {
cmd, msg = v(req)
}
//msgStr := string(req) //msgStr := string(req)
msg := req[8:] //cmd = gconv.Int(msgStr[:8])
cmd := gconv.Int(req[:8]) //msg = []byte(msgStr[8:])
//GetRouter(cmd, conn.Uid, msg)
handler, exist := handlers[cmd] handler, exist := handlers[cmd]
if exist { if exist {
//匹配上路由器 //匹配上路由器
err := handler(conn, msg) err := handler(conn, msg)
g.Log().Error(conn.Ctx, err) if err != nil {
g.Log().Error(conn.Ctx, err)
}
} else { } else {
//fmt.Println("未注册的路由器ID:", cmd) //fmt.Println("未注册的路由器ID:", cmd)
s.Send(conn.Uuid, []byte("未注册的协议号:"+strconv.Itoa(cmd))) s.Send(20000000, conn.Uid, []byte("未注册的协议号:"+strconv.Itoa(cmd)))
s.OnClose(conn) s.OnClose(conn)
return return
} }
} }
//绑定用户编号
func (s *SocketV1) BindUid(conn *WebsocketData, uid int64) {
lock.Lock()
defer lock.Unlock()
cacheKey := fmt.Sprintf("socket:uid:%d", uid)
g.Redis().Set(nil, cacheKey, conn.Uuid)
if conn.Uid == 0 {
conn.Uid = uid
}
}
//解绑用户
func (s *SocketV1) UnBindUid(uid int64) {
lock.Lock()
defer lock.Unlock()
cacheKey := fmt.Sprintf("socket:uid:%d", uid)
g.Redis().Del(nil, cacheKey)
}
// Uid2Uuid 用户编号转uuid唯一标识
func (s *SocketV1) Uid2Uuid(uid int64) (uuid string) {
cacheKey := fmt.Sprintf("socket:uid:%d", uid)
get, _ := g.Redis().Get(nil, cacheKey)
if get.IsNil() {
return
}
uuid = get.String()
//如果不在线了
if !m.Contains(uuid) {
// 解绑用户编号
s.UnBindUid(uid)
return
}
return
}
// SendUuid
//
// @Description:
// @receiver s
// @param uid
// @param data
func (s *SocketV1) SendUuid(code int32, id uuid.UUID, data []byte) {
if !m.Contains(id) {
return
}
conn := m.Get(id).(*WebsocketData)
//前置方法
for _, v := range Byte2Pb {
temp := v(code, data)
data, _ = proto.Marshal(temp)
}
conn.Ws.WriteMessage(s.Type, data)
return
}
// Send // Send
// //
// @Description: // @Description:
// @receiver s // @receiver s
// @param uid // @param uid
// @param data // @param data
// @return err func (s *SocketV1) Send(code int32, uid int64, data []byte) {
func (s *SocketV1) Send(id uuid.UUID, data []byte) (err error) { uuid := s.Uid2Uuid(uid)
if !m.Contains(id) { if uuid == "" {
return return
} }
conn := m.Get(id).(*WebsocketData) if !m.Contains(uuid) {
return
}
conn := m.Get(uuid).(*WebsocketData)
//前置方法
for _, v := range Byte2Pb {
temp := v(code, data)
data, _ = proto.Marshal(temp)
}
conn.Ws.WriteMessage(s.Type, data) conn.Ws.WriteMessage(s.Type, data)
return return
} }
// 批量发送 // 批量发送
func (s *SocketV1) SendAll(data []byte) { func (s *SocketV1) SendAll(code int32, data []byte) {
for _, v := range Byte2Pb {
temp := v(code, data)
data, _ = proto.Marshal(temp)
}
m.Iterator(func(k interface{}, v interface{}) bool { m.Iterator(func(k interface{}, v interface{}) bool {
//fmt.Printf("%v:%v ", k, v)
conn := v.(*WebsocketData) conn := v.(*WebsocketData)
conn.Ws.WriteMessage(s.Type, data) conn.Ws.WriteMessage(s.Type, data)
return true return true
}) })
} }
@@ -185,11 +276,14 @@ func (s *SocketV1) OnClose(conn *WebsocketData) {
for _, connect := range OnCloseHandlers { for _, connect := range OnCloseHandlers {
connect(conn) connect(conn)
} }
uid := conn.Uid
// 可能的后续操作: // 可能的后续操作:
// 1. 更新连接状态或从连接池移除 // 1. 更新连接状态或从连接池移除
// 2. 发送通知或清理关联资源 // 2. 发送通知或清理关联资源
// 3. 执行特定于业务的断开处理 // 3. 执行特定于业务的断开处理
m.Remove(conn.Uuid) m.Remove(conn.Uuid)
if uid > 0 {
s.UnBindUid(uid)
}
conn.Ws.Close() conn.Ws.Close()
} }