From 605197345b84f99f30b8a4c71878160affa2ce33 Mon Sep 17 00:00:00 2001 From: BuildingBlocksLin <835606593@qq.com> Date: Sat, 28 Jun 2025 17:38:22 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BD=91=E7=BB=9C=E5=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Client/a.html | 175 ------------------ Client/web/game.js | 103 +++++++++++ Client/web/index.html | 17 ++ Client/web/style.css | 80 ++++++++ Server/Gateway/app/app.go | 15 +- Server/Gateway/app/websocket.go | 2 +- Server/Gateway/config/config.dev.yaml | 22 ++- Server/Gateway/config/config.go | 33 ++-- Server/Gateway/go.mod | 12 +- Server/Gateway/go.sum | 12 -- Server/Gateway/handler/ws_handler/client.go | 120 +++++++----- Server/Gateway/handler/ws_handler/event.go | 9 +- Server/Gateway/handler/ws_handler/temp.go | 21 +++ Server/Gateway/net/ws_gateway/server.go | 40 +++- Server/common/log/log_zap.go | 2 +- Server/common/net/socket/server.go | 20 +- .../common/net/socket/websocket/websocket.go | 40 ++-- Server/common/net/socket/websocket/wsconn.go | 100 +++------- Server/common/proto/cs/define.proto | 22 +++ Server/common/utils/number.go | 13 ++ 20 files changed, 482 insertions(+), 376 deletions(-) delete mode 100644 Client/a.html create mode 100644 Client/web/game.js create mode 100644 Client/web/index.html create mode 100644 Client/web/style.css create mode 100644 Server/Gateway/handler/ws_handler/temp.go create mode 100644 Server/common/proto/cs/define.proto create mode 100644 Server/common/utils/number.go diff --git a/Client/a.html b/Client/a.html deleted file mode 100644 index a2f6d24..0000000 --- a/Client/a.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - - 游戏方块容器 - - - -
- -
- - \ No newline at end of file diff --git a/Client/web/game.js b/Client/web/game.js new file mode 100644 index 0000000..fcd4cd9 --- /dev/null +++ b/Client/web/game.js @@ -0,0 +1,103 @@ +const { createApp } = Vue; + +const App = { + template: ` +
+
+
+
+
+
+
+ `, + data() { + return { + squares: [ + { connected: false, ws: null }, + { connected: false, ws: null }, + { connected: false, ws: null }, + { connected: false, ws: null } + ] + } + }, + mounted() { + // 为每个正方形建立独立的WebSocket连接 + this.squares.forEach((square, index) => { + this.connectWebSocket(square, index); + }); + }, + methods: { + connectWebSocket(square, index) { + // 替换为实际的WebSocket服务器地址 + const wsUrl = `ws://localhost:8501/?token=${index + 1}`; + + try { + square.ws = new WebSocket(wsUrl); + + square.ws.onopen = () => { + square.connected = true; + console.log(`WebSocket ${index + 1} connected`); + + // 连接建立后发送初始消息 + try { + square.ws.send(JSON.stringify({ + type: "init" + })); + console.log(`Initial message sent to WebSocket ${index + 1}`); + } catch (error) { + console.error(`Failed to send initial message to WebSocket ${index + 1}:`, error); + } + }; + + square.ws.onclose = (e) => { + square.connected = false; + console.log(`WebSocket ${index + 1} disconnected`); + console.log(e.code, e.reason, e.wasClean) + // 尝试重新连接 + // setTimeout(() => this.connectWebSocket(square, index), 1000); + }; + + square.ws.onerror = (error) => { + console.error(`WebSocket ${index + 1} error:`, error); + }; + + square.ws.onmessage = (event) => { + console.log(`WebSocket ${index + 1} message:`, event.data); + // 处理接收到的消息 + try { + const message = JSON.parse(event.data); + const arr = JSON.parse(message.data); + + if (Array.isArray(arr) && arr.length === 2) { + const [x, y] = arr; + console.log(`Creating dot at (${x}, ${y}) for square ${index}`); + + const squareElement = document.querySelectorAll('.square')[index]; + if (!squareElement) { + console.error('Square element not found'); + return; + } + + // 创建圆点元素 + const dot = document.createElement('div'); + dot.className = 'game-dot'; + dot.style.left = `${x}px`; + dot.style.top = `${y}px`; + dot.style.zIndex = '10'; + + // 添加到游戏场景 + squareElement.appendChild(dot); + console.log('Dot added successfully'); + } + } catch (error) { + console.error('Error processing message:', error); + } + }; + } catch (error) { + console.error(`WebSocket ${index + 1} init error:`, error); + } + } + } +}; + +createApp(App).mount('#app'); \ No newline at end of file diff --git a/Client/web/index.html b/Client/web/index.html new file mode 100644 index 0000000..2a58d15 --- /dev/null +++ b/Client/web/index.html @@ -0,0 +1,17 @@ + + + + + + + 游戏方块容器 + + + + + +
+ + + + \ No newline at end of file diff --git a/Client/web/style.css b/Client/web/style.css new file mode 100644 index 0000000..d855d00 --- /dev/null +++ b/Client/web/style.css @@ -0,0 +1,80 @@ +/* 重置默认边距 */ +body, html { + margin: 0; + padding: 0; + height: 100%; + background-color: #f5f5f5; +} + +/* 应用容器填满整个视口并居中 */ +.app-container { + width: 100%; + height: 100vh; + display: flex; + justify-content: center; + align-items: center; +} + +/* 重置body样式 */ +body { + margin: 0; + padding: 0; + overflow: hidden; +} + +/* 网格容器 - 固定2x2布局 */ +.grid-container { + display: grid; + grid-template-columns: 400px 400px; + grid-template-rows: 400px 400px; + gap: 30px; + padding: 40px; + background-color: #e0e0e0; + min-height: 100vh; + width: 100vw; + box-sizing: border-box; + margin: 0 auto; + place-content: center; +} + +/* 正方形样式 */ +.square { + width: 400px; + height: 400px; + background-color: #ffffff; + background-image: + linear-gradient(to right, #ddd 1px, transparent 1px), + linear-gradient(to bottom, #ddd 1px, transparent 1px); + background-size: 20px 20px; + border: 5px solid #555; + border-radius: 15px; + position: relative; + box-shadow: 0 6px 12px rgba(0,0,0,0.15); +} + +/* 连接状态圆点 */ +.connection-dot { + position: absolute; + top: 15px; + right: 15px; + width: 20px; + height: 20px; + border-radius: 50%; + background-color: #ff4444; /* 默认断开状态-红色 */ + transition: background-color 0.3s; +} + +.connection-dot.connected { + background-color: #44ff44; /* 连接状态-绿色 */ +} + +/* 游戏动态圆点 */ +.game-dot { + position: absolute; + width: 10px; + height: 10px; + background-color: red; + border-radius: 50%; + transform: translate(-50%, -50%); + pointer-events: none; +} \ No newline at end of file diff --git a/Server/Gateway/app/app.go b/Server/Gateway/app/app.go index 1948e46..cef5995 100644 --- a/Server/Gateway/app/app.go +++ b/Server/Gateway/app/app.go @@ -9,7 +9,6 @@ import ( "fmt" "gateway/config" "gateway/grpc_server" - "gateway/handler/ws_handler" "github.com/gin-gonic/gin" "github.com/judwhite/go-svc" "runtime/debug" @@ -52,10 +51,16 @@ func (p *Program) Start() error { }() discover.Listen() - p.server = grpc_server.NewServer(config.Get().Grpc.Registry.TTL) - p.server.Init(config.Get().Grpc.Registry.Address, config.Get().Grpc.Registry.Port) - - ws_handler.NewClient(123, nil) + p.server = grpc_server.NewServer(config.Get().Serve.Grpc.TTL) + p.server.Init(config.Get().Serve.Grpc.Address, config.Get().Serve.Grpc.Port) + go func() { + cfg := config.Get() + _ = p.wsServer.Run( + log.GetLogger().Named("gnet"), + fmt.Sprintf("tcp4://0.0.0.0:%v", cfg.Serve.Socket.Web.Port), + true, true, false, false, true, 8, + ) + }() return nil } diff --git a/Server/Gateway/app/websocket.go b/Server/Gateway/app/websocket.go index 09dea50..c4b8988 100644 --- a/Server/Gateway/app/websocket.go +++ b/Server/Gateway/app/websocket.go @@ -25,7 +25,7 @@ func (p *Program) initWsServer(cfg *config.Config) error { //) p.wsServer = websocket.NewWSServer( &ws_gateway.GatewayWsServer{}, - log.GetLogger(), + log.GetLogger().Named("ws_server"), 5*time.Second, ) diff --git a/Server/Gateway/config/config.dev.yaml b/Server/Gateway/config/config.dev.yaml index 8830d6e..637549d 100644 --- a/Server/Gateway/config/config.dev.yaml +++ b/Server/Gateway/config/config.dev.yaml @@ -8,12 +8,22 @@ log: max_backups: 3 max_age: 7 -grpc: - registry: +db: + etcd: + address: [ "10.0.40.9:2379" ] + +serve: + grpc: address: "10.0.40.199" port: 8500 ttl: 20 - -db: - etcd: - address: [ "10.0.40.9:2379" ] \ No newline at end of file + socket: + web: + address: "0.0.0.0" + port: 8501 + raw: + address: "0.0.0.0" + port: 8502 + http: + address: "0.0.0.0" + port: 8503 diff --git a/Server/Gateway/config/config.go b/Server/Gateway/config/config.go index e0214cc..94a8c32 100644 --- a/Server/Gateway/config/config.go +++ b/Server/Gateway/config/config.go @@ -1,10 +1,10 @@ package config type Config struct { - App AppConfig `yaml:"app"` - Log LogConfig `yaml:"log"` - Grpc GrpcConfig `yaml:"grpc"` - DB DBConfig `yaml:"db"` + App *AppConfig `yaml:"app"` + Log *LogConfig `yaml:"log"` + DB *DBConfig `yaml:"db"` + Serve *ServeConfig `yaml:"serve"` } type AppConfig struct { @@ -19,16 +19,25 @@ type LogConfig struct { Level string `yaml:"level"` } -type GrpcConfig struct { - Registry *struct { - Address string `yaml:"address"` - Port int `yaml:"port"` - TTL int64 `yaml:"ttl"` - } `yaml:"registry"` -} - type DBConfig struct { Etcd *struct { Address []string `yaml:"address"` } `yaml:"etcd"` } + +type ServeConfig struct { + Grpc *struct { + AddressConfig + TTL int64 `yaml:"ttl"` + } `yaml:"grpc"` + Socket *struct { + Web *AddressConfig `yaml:"web"` + Raw *AddressConfig `yaml:"raw"` + } `yaml:"socket"` + Http *AddressConfig `yaml:"http"` +} + +type AddressConfig struct { + Address string `yaml:"address"` + Port int `yaml:"port"` +} diff --git a/Server/Gateway/go.mod b/Server/Gateway/go.mod index 19dcc34..1dbf73d 100644 --- a/Server/Gateway/go.mod +++ b/Server/Gateway/go.mod @@ -6,12 +6,9 @@ require ( common v0.0.0-00010101000000-000000000000 github.com/gin-contrib/cors v1.7.6 github.com/gin-gonic/gin v1.10.1 - github.com/gobwas/ws v1.4.0 - github.com/golang/protobuf v1.5.4 github.com/judwhite/go-svc v1.2.1 github.com/spf13/viper v1.20.1 - go.mongodb.org/mongo-driver v1.17.4 - golang.org/x/text v0.26.0 + go.uber.org/zap v1.27.0 google.golang.org/grpc v1.71.1 google.golang.org/protobuf v1.36.6 ) @@ -20,11 +17,9 @@ require ( github.com/bwmarrin/snowflake v0.3.0 // indirect github.com/bytedance/sonic v1.13.3 // indirect github.com/bytedance/sonic/loader v0.2.4 // indirect - github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudwego/base64x v0.1.5 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect - github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/gabriel-vasile/mimetype v1.4.9 // indirect github.com/getsentry/sentry-go v0.34.0 // indirect @@ -35,8 +30,10 @@ require ( github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/gobwas/httphead v0.1.0 // indirect github.com/gobwas/pool v0.2.1 // indirect + github.com/gobwas/ws v1.4.0 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.10 // indirect @@ -48,7 +45,6 @@ require ( github.com/panjf2000/ants/v2 v2.11.3 // indirect github.com/panjf2000/gnet/v2 v2.9.1 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect - github.com/redis/go-redis/v9 v9.10.0 // indirect github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.12.0 // indirect @@ -62,12 +58,12 @@ require ( go.etcd.io/etcd/client/pkg/v3 v3.6.1 // indirect go.etcd.io/etcd/client/v3 v3.6.1 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect golang.org/x/arch v0.18.0 // indirect golang.org/x/crypto v0.39.0 // indirect golang.org/x/net v0.41.0 // indirect golang.org/x/sync v0.15.0 // indirect golang.org/x/sys v0.33.0 // indirect + golang.org/x/text v0.26.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect diff --git a/Server/Gateway/go.sum b/Server/Gateway/go.sum index 7a9a8e0..2ad2ff0 100644 --- a/Server/Gateway/go.sum +++ b/Server/Gateway/go.sum @@ -1,9 +1,5 @@ github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= -github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= -github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= -github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0= github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE= github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0= @@ -11,8 +7,6 @@ github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1 github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= -github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= -github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= @@ -23,8 +17,6 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= @@ -112,8 +104,6 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/redis/go-redis/v9 v9.10.0 h1:FxwK3eV8p/CQa0Ch276C7u2d0eNC9kCmAYQ7mCXCzVs= -github.com/redis/go-redis/v9 v9.10.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= @@ -154,8 +144,6 @@ go.etcd.io/etcd/client/pkg/v3 v3.6.1 h1:CxDVv8ggphmamrXM4Of8aCC8QHzDM4tGcVr9p2BS go.etcd.io/etcd/client/pkg/v3 v3.6.1/go.mod h1:aTkCp+6ixcVTZmrJGa7/Mc5nMNs59PEgBbq+HCmWyMc= go.etcd.io/etcd/client/v3 v3.6.1 h1:KelkcizJGsskUXlsxjVrSmINvMMga0VWwFF0tSPGEP0= go.etcd.io/etcd/client/v3 v3.6.1/go.mod h1:fCbPUdjWNLfx1A6ATo9syUmFVxqHH9bCnPLBZmnLmMY= -go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw= -go.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= diff --git a/Server/Gateway/handler/ws_handler/client.go b/Server/Gateway/handler/ws_handler/client.go index 66a4e00..a47db79 100644 --- a/Server/Gateway/handler/ws_handler/client.go +++ b/Server/Gateway/handler/ws_handler/client.go @@ -3,7 +3,9 @@ package ws_handler import ( "common/log" "common/net/socket" + "common/utils" "context" + "fmt" "go.uber.org/zap" "runtime/debug" "sync" @@ -14,29 +16,90 @@ type Client struct { sync.WaitGroup conn socket.ISocketConn // Socket mailChan chan Event // 邮箱队列 - logger *zap.SugaredLogger - ctx context.Context - cancel context.CancelFunc - heartBeat time.Time + logger *zap.SugaredLogger // 日志 + ctx context.Context // 上下文 + cancel context.CancelFunc // 取消上下文 + heartBeat time.Time // 最后一次心跳 UID int32 } func NewClient(uid int32, conn socket.ISocketConn) *Client { - client := &Client{} - client.UID = uid - client.conn = conn - client.logger = log.GetLogger().Named("uid").With("uid", client.UID) - client.logger.Errorf("错误日志 %v", 1) - - client.heartBeat = time.Now() - client.mailChan = make(chan Event, 1024) + client := &Client{ + UID: uid, + conn: conn, + logger: log.GetLogger().Named(fmt.Sprintf("uid:%v", uid)), + heartBeat: time.Now(), + mailChan: make(chan Event, 1024), + } client.ctx, client.cancel = context.WithCancel(context.Background()) + client.Add(1) go client.Loop() return client } -// CloseClient 关闭客户端 +func (c *Client) Loop() { + defer func() { + if err := recover(); err != nil { + c.logger.Errorf("Client Loop err: %v", err) + debug.PrintStack() + } + }() + defer c.onClose() + t := time.NewTicker(20 * time.Second) + defer t.Stop() + for { + select { + case <-c.ctx.Done(): + return + case evt, ok := <-c.mailChan: + if ok { + c.handle(evt) + } + case <-t.C: + _ = c.conn.Ping() + if time.Now().Sub(c.heartBeat) > 120*time.Second { + return + } + } + } +} + +func (c *Client) OnEvent(event Event) { + defer func() { + if err := recover(); err != nil { + c.logger.Warnf(fmt.Sprintf("send event chan error: %v", err)) + } + }() + select { + case c.mailChan <- event: + default: + c.logger.Warnf("Client mailChan full") + } +} + +func (c *Client) handle(event Event) { + switch e := event.(type) { + case *ClientEvent: + m, err := parseMsg(e.Msg) + if err != nil { + c.logger.Errorf("handle event json.Unmarshal err: %v", err) + c.cancel() + } + c.logger.Infof("收到客户端消息:%+v", *m) + switch m.Type { + case "init": + _ = c.conn.Write(wapMsg(&msg{ + Type: "init", + Data: fmt.Sprintf("[%v,%v]", utils.RandInt(1, 100), utils.RandInt(1, 100)), + })) + } + case *PongEvent: + c.heartBeat = time.Now() + } +} + +// CloseClient 关闭客户端(同步,会等待onClose执行完成) func (c *Client) CloseClient() { if c.cancel != nil { c.cancel() @@ -56,34 +119,3 @@ func (c *Client) onClose() { UserMgr.Delete(c.UID) c.Done() } - -func (c *Client) Loop() { - defer func() { - if err := recover(); err != nil { - c.logger.Errorf("Client Loop err: %v", err) - debug.PrintStack() - } - }() - defer c.onClose() - c.Add(1) - //心跳检测 - hearBeatTicker := time.NewTicker(3000 * time.Millisecond) - for { - select { - case <-c.ctx.Done(): - return - case _, _ = <-c.mailChan: - - case <-hearBeatTicker.C: - // 心跳超时直接关掉连接 - if c.checkHeartBeatTimeout() { - return - } - } - } -} - -func (c *Client) checkHeartBeatTimeout() bool { - sub := time.Now().Sub(c.heartBeat) - return sub > 60*time.Second -} diff --git a/Server/Gateway/handler/ws_handler/event.go b/Server/Gateway/handler/ws_handler/event.go index 9fa9ae4..7a1ef51 100644 --- a/Server/Gateway/handler/ws_handler/event.go +++ b/Server/Gateway/handler/ws_handler/event.go @@ -1,18 +1,13 @@ package ws_handler -import ( - "google.golang.org/protobuf/proto" -) - type Event interface { } -// ClientEvent 客户端发过来的Event type ClientEvent struct { Event - Msg proto.Message + Msg []byte } -type RemoveConnectionEvent struct { +type PongEvent struct { Event } diff --git a/Server/Gateway/handler/ws_handler/temp.go b/Server/Gateway/handler/ws_handler/temp.go new file mode 100644 index 0000000..7a2cdda --- /dev/null +++ b/Server/Gateway/handler/ws_handler/temp.go @@ -0,0 +1,21 @@ +package ws_handler + +import "encoding/json" + +type msg struct { + Type string `json:"type"` + Data string `json:"data"` +} + +func parseMsg(data []byte) (*msg, error) { + m := &msg{} + if err := json.Unmarshal(data, m); err != nil { + return nil, err + } + return m, nil +} + +func wapMsg(m *msg) []byte { + data, _ := json.Marshal(m) + return data +} diff --git a/Server/Gateway/net/ws_gateway/server.go b/Server/Gateway/net/ws_gateway/server.go index 871adaa..f3b475b 100644 --- a/Server/Gateway/net/ws_gateway/server.go +++ b/Server/Gateway/net/ws_gateway/server.go @@ -1,27 +1,59 @@ package ws_gateway import ( + "common/log" "common/net/socket" + "fmt" + "gateway/handler/ws_handler" + "go.uber.org/zap" + "strconv" "time" ) type GatewayWsServer struct { + logger *zap.SugaredLogger } -func (g *GatewayWsServer) OnOpen(_ socket.ISocketConn) ([]byte, socket.Action) { +func (g *GatewayWsServer) OnOpen(conn socket.ISocketConn) ([]byte, socket.Action) { + g.logger = log.GetLogger().Named(fmt.Sprintf("addr:%v", conn.RemoteAddr())) return nil, socket.None } func (g *GatewayWsServer) OnHandShake(conn socket.ISocketConn) { - //query := conn.GetParam("query") + token, ok := conn.GetParam("token").(string) + if !ok || len(token) == 0 { + g.logger.Warnf("token is not string") + _ = conn.Close() + return + } + t, err := strconv.Atoi(token) + if err != nil { + _ = conn.Close() + } + client := ws_handler.NewClient(int32(t), conn) + ws_handler.UserMgr.Add(int32(t), client) + conn.SetParam("client", client) } func (g *GatewayWsServer) OnMessage(conn socket.ISocketConn, bytes []byte) socket.Action { + client, ok := conn.GetParam("client").(*ws_handler.Client) + if !ok || client.UID == 0 { + return socket.Close + } + client.OnEvent(&ws_handler.ClientEvent{Msg: bytes}) return socket.None } -func (g *GatewayWsServer) OnClose(conn socket.ISocketConn, _ error) socket.Action { - return socket.None +func (g *GatewayWsServer) OnPong(conn socket.ISocketConn) { + client, ok := conn.GetParam("client").(*ws_handler.Client) + if !ok || client.UID == 0 { + return + } + client.OnEvent(&ws_handler.PongEvent{}) +} + +func (g *GatewayWsServer) OnClose(_ socket.ISocketConn, _ error) socket.Action { + return socket.Close } func (g *GatewayWsServer) OnTick() (time.Duration, socket.Action) { diff --git a/Server/common/log/log_zap.go b/Server/common/log/log_zap.go index 19d85a5..c9d3278 100644 --- a/Server/common/log/log_zap.go +++ b/Server/common/log/log_zap.go @@ -56,7 +56,7 @@ func Init(debug bool, maxSize, maxBackups, maxAge int, level string) { logger = logger.WithOptions( zap.AddCaller(), zap.AddCallerSkip(1), - zap.AddStacktrace(zapcore.WarnLevel), + zap.AddStacktrace(zapcore.ErrorLevel), ) } SetLogger(logger.Sugar()) diff --git a/Server/common/net/socket/server.go b/Server/common/net/socket/server.go index 1d8e830..2b799f4 100644 --- a/Server/common/net/socket/server.go +++ b/Server/common/net/socket/server.go @@ -15,19 +15,33 @@ const ( Shutdown ) +type OpCode byte + +const ( + OpContinuation OpCode = 0x0 + OpText OpCode = 0x1 + OpBinary OpCode = 0x2 + OpClose OpCode = 0x8 + OpPing OpCode = 0x9 + OpPong OpCode = 0xa +) + // ISocketServer 由应用层实现 type ISocketServer interface { OnOpen(ISocketConn) ([]byte, Action) // 开启连接 OnHandShake(ISocketConn) // 开始握手 OnMessage(ISocketConn, []byte) Action // 收到消息 - OnClose(ISocketConn, error) Action // 关闭连接 + OnPong(ISocketConn) + OnClose(ISocketConn, error) Action // 关闭连接 OnTick() (time.Duration, Action) } // ISocketConn 由网络层实现 type ISocketConn interface { - GetParam(key string) string - SetParam(key string, values string) + GetParam(key string) interface{} + SetParam(key string, values interface{}) + RemoteAddr() string + Ping() error // 需要隔一段时间调用一下,建议20s Write(data []byte) error Close() error } diff --git a/Server/common/net/socket/websocket/websocket.go b/Server/common/net/socket/websocket/websocket.go index fae657e..f402b0d 100644 --- a/Server/common/net/socket/websocket/websocket.go +++ b/Server/common/net/socket/websocket/websocket.go @@ -66,7 +66,8 @@ func (s *WSServer) OnOpen(c gnet.Conn) ([]byte, gnet.Action) { curHeader: nil, cachedBuf: bytes.Buffer{}, }, - param: make(map[string]string), + param: make(map[string]interface{}), + remoteAddr: c.RemoteAddr().String(), } c.SetContext(ws) s.unUpgradeConn.Store(c.RemoteAddr().String(), ws) @@ -78,10 +79,10 @@ func (s *WSServer) OnOpen(c gnet.Conn) ([]byte, gnet.Action) { // The parameter err is the last known connection error. func (s *WSServer) OnClose(c gnet.Conn, err error) (action gnet.Action) { s.unUpgradeConn.Delete(c.RemoteAddr().String()) - tmp := c.Context() - ws, ok := tmp.(*WSConn) + ws, ok := c.Context().(*WSConn) if ok { - s.logger.Warnf("connection close") + ws.isClose = true + ws.logger.Warnf("connection close: %v --------------------------------------------------", err) return gnet.Action(s.i.OnClose(ws, err)) } return @@ -91,18 +92,14 @@ func (s *WSServer) OnClose(c gnet.Conn, err error) (action gnet.Action) { func (s *WSServer) OnTraffic(c gnet.Conn) (action gnet.Action) { tmp := c.Context() if tmp == nil { - if s.logger != nil { - s.logger.Errorf("OnTraffic context nil: %v", c) - } + s.logger.Errorf("OnTraffic context nil: %v", c) action = gnet.Close return } ws, ok := tmp.(*WSConn) if !ok { - if s.logger != nil { - s.logger.Errorf("OnTraffic convert ws error: %v", tmp) - } + ws.logger.Errorf("OnTraffic convert ws error: %v", tmp) action = gnet.Close return } @@ -121,9 +118,7 @@ func (s *WSServer) OnTraffic(c gnet.Conn) (action gnet.Action) { if data != nil { err := ws.Conn.AsyncWrite(data, nil) if err != nil { - if ws.logger != nil { - ws.logger.Errorf("update ws write upgrade protocol error", err) - } + ws.logger.Errorf("update ws write upgrade protocol error", err) action = gnet.Close } } @@ -131,14 +126,22 @@ func (s *WSServer) OnTraffic(c gnet.Conn) (action gnet.Action) { } else { msg, err := ws.readWsMessages() if err != nil { - if ws.logger != nil { - ws.logger.Errorf("read ws messages errors", err) - } + ws.logger.Errorf("read ws messages errors", err) return gnet.Close } if msg != nil { for _, m := range msg { + if socket.OpCode(m.OpCode) == socket.OpPong { + s.i.OnPong(ws) + continue + } + if socket.OpCode(m.OpCode) == socket.OpClose { + return gnet.Close + } + if socket.OpCode(m.OpCode) == socket.OpPing { + continue + } a := s.i.OnMessage(ws, m.Payload) if gnet.Action(a) != gnet.None { action = gnet.Action(a) @@ -182,10 +185,7 @@ func (s *WSServer) OnTick() (delay time.Duration, action gnet.Action) { continue } if err := v.Close(); err != nil { - if s.logger != nil { - s.logger.Errorf("upgrade ws time out close socket error: %v", err) - continue - } + v.logger.Errorf("upgrade ws time out close socket error: %v", err) } } d, a := s.i.OnTick() diff --git a/Server/common/net/socket/websocket/wsconn.go b/Server/common/net/socket/websocket/wsconn.go index f54c265..adefbc2 100644 --- a/Server/common/net/socket/websocket/wsconn.go +++ b/Server/common/net/socket/websocket/wsconn.go @@ -15,12 +15,13 @@ import ( // WSConn 实现ISocketConn接口 type WSConn struct { gnet.Conn - buf bytes.Buffer - logger logging.Logger - isUpgrade bool - isClose bool - param map[string]string - openTime int64 + buf bytes.Buffer + logger logging.Logger + isUpgrade bool + isClose bool + param map[string]interface{} + openTime int64 + remoteAddr string wsMessageBuf } @@ -169,19 +170,24 @@ func (w *WSConn) OnRequest(u []byte) error { return nil } -func (w *WSConn) GetParam(key string) string { +func (w *WSConn) GetParam(key string) interface{} { return w.param[key] } -func (w *WSConn) SetParam(key string, values string) { +func (w *WSConn) SetParam(key string, values interface{}) { w.param[key] = values } +func (w *WSConn) RemoteAddr() string { + return w.remoteAddr +} + func (w *WSConn) Write(data []byte) error { - if w.isClose { - return errors.New("connection has close") - } - return w.write(data, ws.OpBinary) + return w.write(data, ws.OpText) +} + +func (w *WSConn) Ping() (err error) { + return w.write(make([]byte, 0), ws.OpPing) } func (w *WSConn) Close() (err error) { @@ -192,74 +198,12 @@ func (w *WSConn) Close() (err error) { } func (w *WSConn) write(data []byte, opCode ws.OpCode) error { + if w.isClose { + return errors.New("connection has close") + } buf := bytes.Buffer{} - err := wsutil.WriteServerMessage(&buf, opCode, data) - if err != nil { + if err := wsutil.WriteServerMessage(&buf, opCode, data); err != nil { return err } return w.Conn.AsyncWrite(buf.Bytes(), nil) } - -//func (w *WSConn) Pong(data []byte) (err error) { -// buf := bytes.Buffer{} -// if data == nil { -// err = wsutil.WriteServerMessage(&buf, ws.OpPong, emptyPayload) -// } else { -// err = wsutil.WriteServerMessage(&buf, ws.OpPong, data) -// } -// if w.isClose { -// return errors.New("connection has close") -// } -// if err != nil { -// return -// } -// return w.Conn.AsyncWrite(buf.Bytes(), nil) -//} - -//func (w *WSConn) Ping(data []byte) (err error) { -// buf := bytes.Buffer{} -// if data == nil { -// err = wsutil.WriteServerMessage(&buf, ws.OpPing, emptyPayload) -// } else { -// err = wsutil.WriteServerMessage(&buf, ws.OpPing, data) -// } -// if w.isClose { -// return errors.New("connection has close") -// } -// if err != nil { -// return err -// } -// -// return w.Conn.AsyncWrite(buf.Bytes(), nil) -//} - -//func (w *WSConn) GetProperty(key string) (interface{}, error) { -// if w.context[key] == nil { -// return nil, errors.New("not found this key") -// } -// return w.context[key], nil -//} -// -//func (w *WSConn) SetProperty(key string, ctx interface{}) { -// w.context[key] = ctx -//} -// -//func (w *WSConn) RemoveProperty(key string) { -// delete(w.context, key) -//} -// -//func (w *WSConn) GetConnID() uint64 { -// return w.connID -//} -// -//func (w *WSConn) Clear() { -// w.context = make(map[string]interface{}) -//} -// -//func (w *WSConn) GetUrlParam() map[string][]string { -// return w.urlParma -//} -// -//func (w *WSConn) SetUrlParam(key string, values []string) { -// w.urlParma[key] = values -//} diff --git a/Server/common/proto/cs/define.proto b/Server/common/proto/cs/define.proto new file mode 100644 index 0000000..a76efc0 --- /dev/null +++ b/Server/common/proto/cs/define.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; + +option go_package = "common/proto/gen/cs"; +import "common.proto"; + +enum MessageID { + MESSAGE_ID_INVALID = 0; + + // 移动指令 + MESSAGE_TYPE_PLAYER_MOVE = 3; + + // 聊天消息 + MESSAGE_TYPE_CHAT_MESSAGE = 4; + + // 心跳包 + MESSAGE_TYPE_HEARTBEAT = 5; +} + +message Message { + MessageID id = 1; + bytes payload = 2; +} \ No newline at end of file diff --git a/Server/common/utils/number.go b/Server/common/utils/number.go new file mode 100644 index 0000000..1fd4f07 --- /dev/null +++ b/Server/common/utils/number.go @@ -0,0 +1,13 @@ +package utils + +import ( + "math/rand" +) + +// RandInt 生成 [min, max] 范围内的随机整数 +func RandInt(min, max int) int { + if min > max { + min, max = max, min + } + return rand.Intn(max-min+1) + min +}