├── gate_server
├── gateserver.go
└── test_gate_rpc.go
├── conf
├── xingo_cluster_架构.png
├── server.json
├── clusterconf_测试网关有root和http.json
└── clusterconf.json
├── .gitignore
├── admin_server
├── test_admin_http.go
└── test_admin_rpc.go
├── master.go
├── net_server
├── test_net_http.go
├── test_net_rpc.go
├── core
│ ├── player.go
│ └── playermgr.go
├── netserver.go
├── test_net_api.go
└── test_net_api_测试网关有root和http.go
├── test.go
├── LICENSE
├── test_log.go
├── game_server
└── test_game_rpc.go
├── server.go
├── README.md
├── client_test1.go
└── pb
└── msg.pb.go
/gate_server/gateserver.go:
--------------------------------------------------------------------------------
1 | package gate_server
2 |
--------------------------------------------------------------------------------
/conf/xingo_cluster_架构.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/viphxin/xingo_cluster/HEAD/conf/xingo_cluster_架构.png
--------------------------------------------------------------------------------
/conf/server.json:
--------------------------------------------------------------------------------
1 | {
2 | "TcpPort": 8999,
3 | "StepPerMs": 2000,
4 | "PoolSize": 1,
5 | "LogLevel": 2,
6 | "MaxConn": 20000,
7 | "LogPath": "/Users/huangxin/workspace/src/xingo_cluster/log",
8 | "LogName": "cluster.log",
9 | "WriteList": ["127.0.0.1"]
10 | }
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Object files, Static and Dynamic libs (Shared Objects)
2 | *.o
3 | *.a
4 | *.so
5 |
6 | # Folders
7 | _obj
8 | _test
9 |
10 | # Architecture specific extensions/prefixes
11 | *.[568vq]
12 | [568vq].out
13 |
14 | *.cgo1.go
15 | *.cgo2.c
16 | _cgo_defun.c
17 | _cgo_gotypes.go
18 | _cgo_export.*
19 |
20 | _testmain.go
21 |
22 | *.exe
23 | *.test
24 | *.prof
25 | .idea
26 | .log
27 |
--------------------------------------------------------------------------------
/admin_server/test_admin_http.go:
--------------------------------------------------------------------------------
1 | package admin_server
2 |
3 | import (
4 | "net/http"
5 | "github.com/viphxin/xingo/logger"
6 | "strings"
7 | "time"
8 | )
9 |
10 | type TestAdminHttp struct {
11 |
12 | }
13 |
14 | func (this *TestAdminHttp)Hello(w http.ResponseWriter, r *http.Request) {
15 | logger.Info(strings.Repeat("hello", 10))
16 | time.Sleep(3*time.Second)
17 | w.Write([]byte("hudfasdkasfas"))
18 | }
19 |
--------------------------------------------------------------------------------
/master.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "path/filepath"
5 | "github.com/viphxin/xingo"
6 | )
7 |
8 | func main() {
9 | dir, err := filepath.Abs(filepath.Dir("."))
10 | if err == nil{
11 | if true{
12 | s := xingo.NewXingoMaster(filepath.Join(dir, "conf", "clusterconf.json"))
13 | s.StartMaster()
14 | }else{
15 | s := xingo.NewXingoMaster(filepath.Join(dir, "conf", "clusterconf_测试网关有root和http.json"))
16 | s.StartMaster()
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/net_server/test_net_http.go:
--------------------------------------------------------------------------------
1 | package net_server
2 |
3 | import (
4 | "net/http"
5 | "net/url"
6 | "fmt"
7 | )
8 |
9 | type TestNetHttp struct {
10 |
11 | }
12 |
13 | func (this *TestNetHttp)Hello(w http.ResponseWriter, r *http.Request) {
14 | queryForm, err := url.ParseQuery(r.URL.RawQuery)
15 | if err == nil && len(queryForm["name"]) > 0 {
16 | w.Write([]byte(fmt.Sprintf("hello %s", queryForm["name"][0])))
17 | }else{
18 | w.Write([]byte("hello 陌生人"))
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/conf/clusterconf_测试网关有root和http.json:
--------------------------------------------------------------------------------
1 | {
2 | "master":{"host": "127.0.0.1","rootport":9999, "debugport":8881, "writelist": ["127.0.0.1"]},
3 | "servers":{
4 | "net1":{"host": "127.0.0.1", "netport":11009, "rootport":11109, "http": [11209, "/static"], "debugport":11007, "name":"net1", "module": "net", "log": "net.log"},
5 | "net2":{"host": "127.0.0.1", "netport":11010, "rootport":11110, "http": [11210, "/static"], "name":"net2", "module": "net", "log": "net.log"},
6 | "game1":{"host": "127.0.0.1", "remotes":["net1", "net2"], "name":"game1", "module": "game"}
7 | }
8 | }
--------------------------------------------------------------------------------
/net_server/test_net_rpc.go:
--------------------------------------------------------------------------------
1 | package net_server
2 |
3 | import (
4 | "github.com/viphxin/xingo/cluster"
5 | "xingo_cluster/pb"
6 | )
7 |
8 | type TestNetRpc struct {
9 |
10 | }
11 |
12 | func (this *TestNetRpc)PushMsg2Client(request *cluster.RpcRequest){
13 | pid := request.Rpcdata.Args[0].(int32)
14 | message := request.Rpcdata.Args[1].(string)
15 | p := GlobalPlayerMgr.GetPlayer(pid)
16 | if p != nil{
17 | msg := &pb.BroadCast{
18 | Pid : pid,
19 | Tp: 1,
20 | Data: &pb.BroadCast_Content{
21 | Content: message,
22 | },
23 | }
24 | p.SendMsg(1, msg)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/admin_server/test_admin_rpc.go:
--------------------------------------------------------------------------------
1 | package admin_server
2 |
3 | import (
4 | "github.com/viphxin/xingo/clusterserver"
5 | "github.com/viphxin/xingo/cluster"
6 | "github.com/viphxin/xingo/logger"
7 | )
8 |
9 | type TestAdminRpc struct {
10 |
11 | }
12 |
13 | func (this *TestAdminRpc)GetGSTime(request *cluster.RpcRequest){
14 | pid := (request.Rpcdata.Args[0]).(int32)
15 | //转发到gate
16 | onegate := clusterserver.GlobalClusterServer.RemoteNodesMgr.GetRandomChild("gate")
17 |
18 | if onegate != nil{
19 | logger.Debug("chose root: " + onegate.GetName())
20 | onegate.CallChildNotForResult("Proxy2Game", pid, "2222.3333")
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/net_server/core/player.go:
--------------------------------------------------------------------------------
1 | package core
2 |
3 | import (
4 | "github.com/golang/protobuf/proto"
5 | "github.com/viphxin/xingo/iface"
6 | "github.com/viphxin/xingo/logger"
7 | "github.com/viphxin/xingo/utils"
8 | )
9 |
10 | type Player struct {
11 | Pid int32
12 | Fconn iface.Iconnection
13 | }
14 |
15 | func NewPlayer(pid int32, fconn iface.Iconnection) *Player {
16 | return &Player{
17 | Pid: pid,
18 | Fconn: fconn,
19 | }
20 | }
21 |
22 | func (this *Player) SendMsg(msgId uint32, data proto.Message) {
23 | if this.Fconn != nil {
24 | packdata, err := utils.GlobalObject.Protoc.GetDataPack().Pack(msgId, data)
25 | if err == nil {
26 | this.Fconn.Send(packdata)
27 | } else {
28 | logger.Error("pack data error")
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/net_server/netserver.go:
--------------------------------------------------------------------------------
1 | package net_server
2 |
3 | import (
4 | "github.com/viphxin/xingo/iface"
5 | "github.com/viphxin/xingo/logger"
6 | "github.com/viphxin/xingo/utils"
7 | "xingo_cluster/net_server/core"
8 | )
9 |
10 | func DoConnectionMade(fconn iface.Iconnection) {
11 | logger.Debug("111111111111111111111111")
12 | p := GlobalPlayerMgr.Add(fconn)
13 | fconn.SetProperty("pid", p.Pid)
14 | }
15 |
16 | func DoConnectionLost(fconn iface.Iconnection) {
17 | logger.Debug("222222222222222222222222")
18 | pid, err := fconn.GetProperty("pid")
19 | if err == nil {
20 | GlobalPlayerMgr.Remove(pid.(int32))
21 | }
22 |
23 | }
24 |
25 | var GlobalPlayerMgr *core.PlayerMgr = core.NewPlayerMgr()
26 |
27 | func init() {
28 | utils.GlobalObject.OnConnectioned = DoConnectionMade
29 | utils.GlobalObject.OnClosed = DoConnectionLost
30 | }
31 |
--------------------------------------------------------------------------------
/net_server/core/playermgr.go:
--------------------------------------------------------------------------------
1 | package core
2 |
3 | import (
4 | "sync"
5 | "github.com/viphxin/xingo/iface"
6 | )
7 |
8 | type PlayerMgr struct {
9 | sync.RWMutex
10 | players map[int32]*Player
11 | }
12 |
13 | func NewPlayerMgr() *PlayerMgr{
14 | return &PlayerMgr{
15 | players: make(map[int32]*Player, 0),
16 | }
17 | }
18 |
19 | func (this *PlayerMgr)Add(fconn iface.Iconnection) *Player{
20 | this.Lock()
21 | defer this.Unlock()
22 |
23 | p := NewPlayer(int32(fconn.GetSessionId()), fconn)
24 | this.players[p.Pid] = p
25 | return p
26 | }
27 |
28 | func (this *PlayerMgr)Remove(pid int32){
29 | this.Lock()
30 | defer this.Unlock()
31 |
32 | delete(this.players, pid)
33 | }
34 |
35 | func (this *PlayerMgr)GetPlayer(pid int32) *Player{
36 | this.RLock()
37 | defer this.RUnlock()
38 |
39 | p, ok := this.players[pid]
40 | if ok{
41 | return p
42 | }else{
43 | return nil
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/conf/clusterconf.json:
--------------------------------------------------------------------------------
1 | {
2 | "master":{"host": "127.0.0.1","rootport":9999, "debugport":8881},
3 | "servers":{
4 | "gate1":{"host": "127.0.0.1", "rootport":10000,"name":"gate1", "module": "gate", "log": "gate.log"},
5 | "gate2":{"host": "127.0.0.1", "rootport":10001,"name":"gate2", "module": "gate", "log": "gate.log"},
6 | "net1":{"host": "127.0.0.1", "netport":11009,"debugport":11007,"name":"net1","remotes":["gate1", "gate2"], "module": "net", "log": "net.log"},
7 | "net2":{"host": "127.0.0.1", "netport":11010,"name":"net2","remotes":["gate1", "gate2"], "module": "net", "log": "net.log"},
8 | "net3":{"host": "127.0.0.1", "netport":11011,"name":"net3","remotes":["gate1", "gate2"], "module": "net", "log": "net.log"},
9 | "net4":{"host": "127.0.0.1", "netport":11012,"name":"net4","remotes":["gate1", "gate2"], "module": "net", "log": "net.log"},
10 | "admin1":{"host": "127.0.0.1", "remotes":["gate2", "gate1"], "name":"admin1", "module": "admin", "http": [8888, "/static"]},
11 | "game1":{"host": "127.0.0.1", "remotes":["gate2", "gate1"], "name":"game1", "module": "game"}
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "reflect"
5 | "github.com/viphxin/xingo/cluster"
6 | "encoding/json"
7 | "fmt"
8 | "runtime/debug"
9 | "time"
10 | )
11 |
12 | func main() {
13 | type RpcData struct {
14 | MsgType cluster.RpcSignal `json:"msgtype"`
15 | Key string `json:"key,omitempty"`
16 | Target string `json:"target,omitempty"`
17 | Args []interface{} `json:"args,omitempty"`
18 | Result []reflect.Value `json:"result,omitempty"`
19 | }
20 |
21 | a := &RpcData{
22 | MsgType: cluster.REQUEST_NORESULT,
23 | Key: "dasdasdsa",
24 | }
25 | bbb, err := json.Marshal(a)
26 | if err != nil{
27 | fmt.Println("error")
28 | fmt.Println(err)
29 | }else{
30 | aa := &RpcData{}
31 | err = json.Unmarshal(bbb, aa)
32 | if err != nil{
33 | println(err)
34 | }else{
35 | println(aa.Key, aa.MsgType)
36 | }
37 | }
38 | defer func() {
39 | if err := recover(); err != nil {
40 | debug.PrintStack()
41 | println("panic recover!!!")
42 | }
43 | }()
44 | for {
45 | println("sdasdsadas1111111")
46 | panic("huangxinsss")
47 | time.Sleep(3*time.Second)
48 | }
49 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 viphxin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/test_log.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "runtime"
5 | "strconv"
6 | "github.com/viphxin/xingo/logger"
7 | "path/filepath"
8 | )
9 |
10 | func runlog(i int) {
11 | logger.Debug("Debug>>>>>>>>>>>>>>>>>>>>>>" + strconv.Itoa(i))
12 | logger.Info("Info>>>>>>>>>>>>>>>>>>>>>>>>>" + strconv.Itoa(i))
13 | logger.Warn("Warn>>>>>>>>>>>>>>>>>>>>>>>>>" + strconv.Itoa(i))
14 | logger.Error("Error>>>>>>>>>>>>>>>>>>>>>>>>>" + strconv.Itoa(i))
15 | logger.Fatal("Fatal>>>>>>>>>>>>>>>>>>>>>>>>>" + strconv.Itoa(i))
16 | }
17 |
18 | func main() {
19 | runtime.GOMAXPROCS(runtime.NumCPU())
20 |
21 | //指定是否控制台打印,默认为true
22 | logger.SetConsole(true)
23 | //指定日志文件备份方式为文件大小的方式
24 | //第一个参数为日志文件存放目录
25 | //第二个参数为日志文件命名
26 | //第三个参数为备份文件最大数量
27 | //第四个参数为备份文件大小
28 | //第五个参数为文件大小的单位
29 | //logger.SetRollingFile("d:/logtest", "test.log", 10, 5, logger.KB)
30 |
31 | //指定日志文件备份方式为日期的方式
32 | //第一个参数为日志文件存放目录
33 | //第二个参数为日志文件命名
34 | p, _ := filepath.Abs("./log")
35 | print(p)
36 | //logger.SetRollingDaily(filepath.Dir("./log"), "test111.log")
37 |
38 | //指定日志级别 ALL,DEBUG,INFO,WARN,ERROR,FATAL,OFF 级别由低到高
39 | //一般习惯是测试阶段为debug,生成环境为info以上
40 | //logger.SetLevel(logger.ERROR)
41 | //
42 | //for i := 100; i > 0; i-- {
43 | // go runlog(i)
44 | // time.Sleep(1000 * time.Millisecond)
45 | //}
46 | //time.Sleep(15 * time.Second)
47 | }
--------------------------------------------------------------------------------
/game_server/test_game_rpc.go:
--------------------------------------------------------------------------------
1 | package game_server
2 |
3 | import (
4 | "github.com/viphxin/xingo/cluster"
5 | "github.com/viphxin/xingo/logger"
6 | _"github.com/viphxin/xingo/clusterserver"
7 | "github.com/viphxin/xingo/clusterserver"
8 | "fmt"
9 | "strings"
10 | )
11 |
12 | type TestGameRpc struct {
13 |
14 | }
15 |
16 | func (this *TestGameRpc)Proxy2Game(request *cluster.RpcRequest){
17 | //Json反序列化数字到interface{}类型的值中,默认解析为float64类型,在使用时要注意
18 | pid := int32((request.Rpcdata.Args[0]).(float64))
19 | message := (request.Rpcdata.Args[1]).(string)
20 | logger.Debug(pid, message)
21 | //onenet := clusterserver.GlobalClusterServer.ChildsMgr.GetRandomChild("net")
22 | //if onenet != nil{
23 | // onenet.CallChildNotForResult("PushMsg2Client", pid, message)
24 | //}
25 | for _, child := range clusterserver.GlobalClusterServer.RemoteNodesMgr.GetChilds(){
26 | if strings.Contains(child.GetName(), "net"){
27 | child.CallChildNotForResult("PushMsg2Client", pid, message)
28 | }
29 | }
30 | }
31 |
32 | func (this *TestGameRpc)Add(request *cluster.RpcRequest) map[string]interface{}{
33 | //Json反序列化数字到interface{}类型的值中,默认解析为float64类型,在使用时要注意
34 | i := int32((request.Rpcdata.Args[0]).(float64))
35 | ii := int32((request.Rpcdata.Args[1]).(float64))
36 |
37 | logger.Debug(fmt.Sprintf("%d + %d = %d", i, ii, i + ii))
38 | return map[string]interface{}{
39 | "sum": i + ii,
40 | }
41 | }
--------------------------------------------------------------------------------
/server.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "os"
5 | "path/filepath"
6 | "xingo_cluster/net_server"
7 | "xingo_cluster/gate_server"
8 | "xingo_cluster/admin_server"
9 | _ "net/http"
10 | _ "net/http/pprof"
11 | "github.com/viphxin/xingo"
12 | "xingo_cluster/game_server"
13 | )
14 |
15 | func main() {
16 | //pprof
17 | //go func() {
18 | // println(http.ListenAndServe("localhost:6060", nil))
19 | //}()
20 |
21 | //server code
22 | args := os.Args
23 | dir, err := filepath.Abs(filepath.Dir("."))
24 | if err == nil{
25 | if true{
26 | s := xingo.NewXingoCluterServer(args[1], filepath.Join(dir, "conf", "clusterconf.json"))
27 | /*
28 | 注册分布式服务器
29 | */
30 | //net server
31 | s.AddModule("net", &net_server.TestNetApi{}, nil, &net_server.TestNetRpc{})
32 | //gate server
33 | s.AddModule("gate", nil, nil, &gate_server.TestGateRpc{})
34 | //admin server
35 | s.AddModule("admin", nil, &admin_server.TestAdminHttp{}, &admin_server.TestAdminRpc{})
36 |
37 | s.StartClusterServer()
38 | }else{
39 | s := xingo.NewXingoCluterServer(args[1], filepath.Join(dir, "conf", "clusterconf_测试网关有root和http.json"))
40 | /*
41 | 注册分布式服务器
42 | */
43 | //net server
44 | s.AddModule("net", &net_server.TestNetApi2{}, &net_server.TestNetHttp{}, &net_server.TestNetRpc{})
45 | //game server
46 | s.AddModule("game", nil, nil, &game_server.TestGameRpc{})
47 |
48 | s.StartClusterServer()
49 | }
50 |
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/gate_server/test_gate_rpc.go:
--------------------------------------------------------------------------------
1 | package gate_server
2 |
3 | import (
4 | "github.com/viphxin/xingo/cluster"
5 | "github.com/viphxin/xingo/logger"
6 | _"github.com/viphxin/xingo/clusterserver"
7 | "github.com/viphxin/xingo/clusterserver"
8 | "fmt"
9 | "strings"
10 | )
11 |
12 | type TestGateRpc struct {
13 |
14 | }
15 |
16 | func (this *TestGateRpc)Proxy2Game(request *cluster.RpcRequest){
17 | //Json反序列化数字到interface{}类型的值中,默认解析为float64类型,在使用时要注意
18 | pid := (request.Rpcdata.Args[0]).(int32)
19 | message := (request.Rpcdata.Args[1]).(string)
20 | logger.Debug(pid, message)
21 | //onenet := clusterserver.GlobalClusterServer.ChildsMgr.GetRandomChild("net")
22 | //if onenet != nil{
23 | // onenet.CallChildNotForResult("PushMsg2Client", pid, message)
24 | //}
25 | for _, child := range clusterserver.GlobalClusterServer.ChildsMgr.GetChilds(){
26 | if strings.Contains(child.GetName(), "net"){
27 | child.CallChildNotForResult("PushMsg2Client", pid, message)
28 | }
29 | }
30 | }
31 |
32 | func (this *TestGateRpc)Add(request *cluster.RpcRequest) map[string]interface{}{
33 | //Json反序列化数字到interface{}类型的值中,默认解析为float64类型,在使用时要注意
34 | i := (request.Rpcdata.Args[0]).(int)
35 | ii := (request.Rpcdata.Args[1]).(int)
36 |
37 | logger.Debug(fmt.Sprintf("%d + %d = %d", i, ii, i + ii))
38 | return map[string]interface{}{
39 | "sum": i + ii,
40 | }
41 | }
42 |
43 | func (this *TestGateRpc)GetGSTime(request *cluster.RpcRequest){
44 | //Json反序列化数字到interface{}类型的值中,默认解析为float64类型,在使用时要注意
45 | pid := (request.Rpcdata.Args[0]).(int32)
46 | onenet := clusterserver.GlobalClusterServer.ChildsMgr.GetRandomChild("admin")
47 | logger.Debug(onenet)
48 | if onenet != nil{
49 | onenet.CallChildNotForResult("GetGSTime", pid)
50 | }
51 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # xingo_cluster
2 | xingo地址:(https://git.oschina.net/viphxin/xingo)
3 | xingo cluster 分布式集群 示例代码
4 | ```json
5 | 启动服务器集群:
6 | go run master.go
7 | go run server.go gate1
8 | go run server.go gate2
9 | go run server.go net1
10 | go run server.go net2
11 | go run server.go net3
12 | go run server.go net4
13 | go run server.go admin
14 |
15 | 启动测试脚本:
16 | go run client_test1.go
17 | ```
18 | 示例配置:
19 | ```json
20 | {
21 | "master":{"host": "192.168.2.225","rootport":9999},
22 | "servers":{
23 | "gate2":{"host": "192.168.2.225", "rootport":10000,"name":"gate2", "module": "gate", "log": "gate2.log"},
24 | "gate1":{"host": "192.168.2.225", "rootport":10001,"name":"gate1", "module": "gate", "log": "gate1.log"},
25 | "net1":{"host": "192.168.2.225", "netport":11009,"name":"net1","remotes":["gate2", "gate1"],
26 | "module": "net", "log": "net.log"},
27 | "net2":{"host": "192.168.2.225", "netport":11010,"name":"net2","remotes":["gate2", "gate1"],
28 | "module": "net", "log": "net.log"},
29 | "net3":{"host": "192.168.2.225", "netport":11011,"name":"net3","remotes":["gate2", "gate1"],
30 | "module": "net", "log": "net.log"},
31 | "net4":{"host": "192.168.2.225", "netport":11012,"name":"net4","remotes":["gate2", "gate1"],
32 | "module": "net", "log": "net.log"},
33 | "admin":{"host": "192.168.2.225", "remotes":["gate2", "gate1"], "name":"admin", "module": "admin",
34 | "http": [8888, "/static"]},
35 | "game1":{"host": "192.168.2.225", "remotes":["gate2", "gate1"], "name":"game1", "module": "game"}
36 | }
37 | }
38 | ```
39 | 架构图:
40 | 
41 |
--------------------------------------------------------------------------------
/net_server/test_net_api.go:
--------------------------------------------------------------------------------
1 | package net_server
2 |
3 | import (
4 | "github.com/viphxin/xingo/fnet"
5 | "github.com/golang/protobuf/proto"
6 | "fmt"
7 | "xingo_cluster/pb"
8 | "github.com/viphxin/xingo/logger"
9 | "github.com/viphxin/xingo/clusterserver"
10 | "math/rand"
11 | )
12 |
13 | type TestNetApi struct {
14 |
15 | }
16 |
17 | func (this *TestNetApi)Api_0(request *fnet.PkgAll){
18 | msg := &pb.Talk{}
19 | err := proto.Unmarshal(request.Pdata.Data, msg)
20 | if err == nil {
21 | logger.Debug(fmt.Sprintf("user talk: content: %s.", msg.Content))
22 | pid, err1 := request.Fconn.GetProperty("pid")
23 | if err1 == nil{
24 | //转发到gate
25 | onegate := clusterserver.GlobalClusterServer.RemoteNodesMgr.GetRandomChild("gate")
26 |
27 | if onegate != nil{
28 | logger.Debug("chose root: " + onegate.GetName())
29 | onegate.CallChildNotForResult("Proxy2Game", pid.(int32), msg.Content)
30 | }
31 | }else{
32 | logger.Error(err1)
33 | request.Fconn.LostConnection()
34 | }
35 |
36 | } else {
37 | logger.Error(err)
38 | request.Fconn.LostConnection()
39 | }
40 | }
41 |
42 | func (this *TestNetApi)Api_10(request *fnet.PkgAll){
43 | //test rpc for result
44 | //转发到gate
45 | onegate := clusterserver.GlobalClusterServer.RemoteNodesMgr.GetRandomChild("gate")
46 |
47 | if onegate != nil{
48 | logger.Debug("chose root: " + onegate.GetName())
49 | i := rand.Intn(10)
50 | ii := rand.Intn(10)
51 | response, err := onegate.CallChildForResult("Add", i, ii)
52 | if err == nil{
53 | pid, _ := request.Fconn.GetProperty("pid")
54 | p := GlobalPlayerMgr.GetPlayer(pid.(int32))
55 | if p != nil{
56 | msg := &pb.BroadCast{
57 | Pid : pid.(int32),
58 | Tp: 1,
59 | Data: &pb.BroadCast_Content{
60 | Content: fmt.Sprintf("%d + %d = %d", i, ii, response.Result["sum"].(int)),
61 | },
62 | }
63 | p.SendMsg(10, msg)
64 | }
65 | }else{
66 | logger.Error(err)
67 | }
68 | }
69 | }
70 |
71 | func (this *TestNetApi)Api_11(request *fnet.PkgAll){
72 | //test rpc for result
73 | //转发到gate
74 | onegate := clusterserver.GlobalClusterServer.RemoteNodesMgr.GetRandomChild("gate")
75 |
76 | if onegate != nil{
77 | pid, _ := request.Fconn.GetProperty("pid")
78 | onegate.CallChildNotForResult("GetGSTime", pid)
79 |
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/net_server/test_net_api_测试网关有root和http.go:
--------------------------------------------------------------------------------
1 | package net_server
2 |
3 | import (
4 | "github.com/viphxin/xingo/fnet"
5 | "github.com/golang/protobuf/proto"
6 | "fmt"
7 | "xingo_cluster/pb"
8 | "github.com/viphxin/xingo/logger"
9 | "github.com/viphxin/xingo/clusterserver"
10 | "math/rand"
11 | )
12 |
13 | type TestNetApi2 struct {
14 |
15 | }
16 |
17 | func (this *TestNetApi2)Api_0(request *fnet.PkgAll){
18 | msg := &pb.Talk{}
19 | err := proto.Unmarshal(request.Pdata.Data, msg)
20 | if err == nil {
21 | logger.Debug(fmt.Sprintf("user talk: content: %s.", msg.Content))
22 | pid, err1 := request.Fconn.GetProperty("pid")
23 | if err1 == nil{
24 | //转发到game
25 | onegame := clusterserver.GlobalClusterServer.ChildsMgr.GetRandomChild("game")
26 |
27 | if onegame != nil{
28 | logger.Debug("chose root: " + onegame.GetName())
29 | onegame.CallChildNotForResult("Proxy2Game", pid.(int32), msg.Content)
30 | }
31 | }else{
32 | logger.Error(err1)
33 | request.Fconn.LostConnection()
34 | }
35 |
36 | } else {
37 | logger.Error(err)
38 | request.Fconn.LostConnection()
39 | }
40 | }
41 |
42 | func (this *TestNetApi2)Api_10(request *fnet.PkgAll){
43 | //test rpc for result
44 | //转发到gate
45 | onegame := clusterserver.GlobalClusterServer.ChildsMgr.GetRandomChild("game")
46 |
47 | if onegame != nil{
48 | logger.Debug("chose child: " + onegame.GetName())
49 | i := rand.Intn(10)
50 | ii := rand.Intn(10)
51 | response, err := onegame.CallChildForResult("Add", i, ii)
52 | if err == nil{
53 | pid, _ := request.Fconn.GetProperty("pid")
54 | p := GlobalPlayerMgr.GetPlayer(pid.(int32))
55 | if p != nil{
56 | msg := &pb.BroadCast{
57 | Pid : pid.(int32),
58 | Tp: 1,
59 | Data: &pb.BroadCast_Content{
60 | Content: fmt.Sprintf("%d + %d = %d", i, ii, response.Result["sum"].(int)),
61 | },
62 | }
63 | p.SendMsg(10, msg)
64 | }
65 | }else{
66 | logger.Error(err)
67 | }
68 | }
69 | }
70 |
71 | func (this *TestNetApi2)Api_11(request *fnet.PkgAll){
72 | //test rpc for result
73 | //转发到gate
74 | onegame := clusterserver.GlobalClusterServer.ChildsMgr.GetRandomChild("game")
75 |
76 | if onegame != nil{
77 | pid, _ := request.Fconn.GetProperty("pid")
78 | onegame.CallChildNotForResult("GetGSTime", pid)
79 |
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/client_test1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "encoding/binary"
6 | "fmt"
7 | "github.com/golang/protobuf/proto"
8 | "github.com/viphxin/xingo/fnet"
9 | "github.com/viphxin/xingo/iface"
10 | "io"
11 | "math/rand"
12 | "os"
13 | "os/signal"
14 | "time"
15 | "xingo_demo/pb"
16 | )
17 |
18 | type PkgData struct {
19 | Len uint32
20 | MsgId uint32
21 | Data []byte
22 | }
23 |
24 | type MyPtotoc struct {
25 | Pid int32
26 | }
27 |
28 | func (this *MyPtotoc) OnConnectionMade(fconn iface.Iclient) {
29 | fmt.Println("链接建立")
30 | go func() {
31 | for {
32 | msg := &pb.Talk{
33 | Content: "哈哈哈哈哈哈哈哈哈哈sdadasdasfas 萨达萨达撒发送的发大声大声",
34 | }
35 | this.Send(fconn, 0, msg)
36 | this.Send(fconn, 10, nil)
37 | this.Send(fconn, 11, nil)
38 | time.Sleep(2000 * time.Millisecond)
39 | }
40 | }()
41 | }
42 |
43 | func (this *MyPtotoc) OnConnectionLost(fconn iface.Iclient) {
44 | fmt.Println("链接丢失")
45 | }
46 |
47 | func (this *MyPtotoc) Unpack(headdata []byte) (head *PkgData, err error) {
48 | headbuf := bytes.NewReader(headdata)
49 |
50 | head = &PkgData{}
51 |
52 | // 读取Len
53 | if err = binary.Read(headbuf, binary.LittleEndian, &head.Len); err != nil {
54 | return nil, err
55 | }
56 |
57 | // 读取MsgId
58 | if err = binary.Read(headbuf, binary.LittleEndian, &head.MsgId); err != nil {
59 | return nil, err
60 | }
61 |
62 | // 封包太大
63 | //if head.Len > MaxPacketSize {
64 | // return nil, packageTooBig
65 | //}
66 |
67 | return head, nil
68 | }
69 |
70 | func (this *MyPtotoc) Pack(msgId uint32, data proto.Message) (out []byte, err error) {
71 | outbuff := bytes.NewBuffer([]byte{})
72 | // 进行编码
73 | dataBytes := []byte{}
74 | if data != nil {
75 | dataBytes, err = proto.Marshal(data)
76 | }
77 |
78 | if err != nil {
79 | fmt.Println(fmt.Sprintf("marshaling error: %s", err))
80 | }
81 | // 写Len
82 | if err = binary.Write(outbuff, binary.LittleEndian, uint32(len(dataBytes))); err != nil {
83 | return
84 | }
85 | // 写MsgId
86 | if err = binary.Write(outbuff, binary.LittleEndian, msgId); err != nil {
87 | return
88 | }
89 |
90 | //all pkg data
91 | if err = binary.Write(outbuff, binary.LittleEndian, dataBytes); err != nil {
92 | return
93 | }
94 |
95 | out = outbuff.Bytes()
96 | return
97 |
98 | }
99 |
100 | func (this *MyPtotoc) DoMsg(fconn iface.Iclient, pdata *PkgData) {
101 | //处理消息
102 | fmt.Println(fmt.Sprintf("msg id :%d, data len: %d", pdata.MsgId, pdata.Len))
103 | if pdata.MsgId == 1 || pdata.MsgId == 10 {
104 | bdata := &pb.BroadCast{}
105 | proto.Unmarshal(pdata.Data, bdata)
106 | println(bdata.GetContent())
107 | }
108 | }
109 |
110 | func (this *MyPtotoc) Send(fconn iface.Iclient, msgID uint32, data proto.Message) {
111 | dd, err := this.Pack(msgID, data)
112 | if err == nil {
113 | fconn.Send(dd)
114 | } else {
115 | fmt.Println(err)
116 | }
117 |
118 | }
119 |
120 | func (this *MyPtotoc) StartReadThread(fconn iface.Iclient) {
121 | go func() {
122 | for {
123 | //read per head data
124 | headdata := make([]byte, 8)
125 |
126 | if _, err := io.ReadFull(fconn.GetConnection(), headdata); err != nil {
127 | fmt.Println(err)
128 | this.OnConnectionLost(fconn)
129 | return
130 | }
131 | pkgHead, err := this.Unpack(headdata)
132 | if err != nil {
133 | this.OnConnectionLost(fconn)
134 | return
135 | }
136 | //data
137 | if pkgHead.Len > 0 {
138 | pkgHead.Data = make([]byte, pkgHead.Len)
139 | if _, err := io.ReadFull(fconn.GetConnection(), pkgHead.Data); err != nil {
140 | this.OnConnectionLost(fconn)
141 | return
142 | }
143 | }
144 | this.DoMsg(fconn, pkgHead)
145 | }
146 | }()
147 | }
148 |
149 | func (this *MyPtotoc) AddRpcRouter(router interface{}) {
150 |
151 | }
152 |
153 | func (this *MyPtotoc) GetMsgHandle() iface.Imsghandle {
154 | return nil
155 | }
156 |
157 | func (this *MyPtotoc) GetDataPack() iface.Idatapack {
158 | return nil
159 | }
160 |
161 | func (this *MyPtotoc) InitWorker(int32) {
162 |
163 | }
164 |
165 | func main() {
166 | nets := []int{11009}
167 | for i := 0; i < 1; i++ {
168 | client := fnet.NewTcpClient("127.0.0.1", nets[rand.Intn(len(nets))], &MyPtotoc{})
169 | client.Start()
170 | time.Sleep(100 * time.Millisecond)
171 | }
172 |
173 | // close
174 | c := make(chan os.Signal, 1)
175 | signal.Notify(c, os.Interrupt, os.Kill)
176 | sig := <-c
177 | fmt.Println("=======", sig)
178 | }
179 |
--------------------------------------------------------------------------------
/pb/msg.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go.
2 | // source: msg.proto
3 | // DO NOT EDIT!
4 |
5 | /*
6 | Package pb is a generated protocol buffer package.
7 |
8 | It is generated from these files:
9 | msg.proto
10 |
11 | It has these top-level messages:
12 | SyncPid
13 | Player
14 | SyncPlayers
15 | Position
16 | MovePackege
17 | BroadCast
18 | Talk
19 | */
20 | package pb
21 |
22 | import proto "github.com/golang/protobuf/proto"
23 | import fmt "fmt"
24 | import math "math"
25 |
26 | // Reference imports to suppress errors if they are not otherwise used.
27 | var _ = proto.Marshal
28 | var _ = fmt.Errorf
29 | var _ = math.Inf
30 |
31 | // This is a compile-time assertion to ensure that this generated file
32 | // is compatible with the proto package it is being compiled against.
33 | // A compilation error at this line likely means your copy of the
34 | // proto package needs to be updated.
35 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
36 |
37 | type SyncPid struct {
38 | Pid int32 `protobuf:"varint,1,opt,name=Pid,json=pid" json:"Pid,omitempty"`
39 | }
40 |
41 | func (m *SyncPid) Reset() { *m = SyncPid{} }
42 | func (m *SyncPid) String() string { return proto.CompactTextString(m) }
43 | func (*SyncPid) ProtoMessage() {}
44 | func (*SyncPid) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
45 |
46 | type Player struct {
47 | Pid int32 `protobuf:"varint,1,opt,name=Pid,json=pid" json:"Pid,omitempty"`
48 | P *Position `protobuf:"bytes,2,opt,name=P,json=p" json:"P,omitempty"`
49 | }
50 |
51 | func (m *Player) Reset() { *m = Player{} }
52 | func (m *Player) String() string { return proto.CompactTextString(m) }
53 | func (*Player) ProtoMessage() {}
54 | func (*Player) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
55 |
56 | func (m *Player) GetP() *Position {
57 | if m != nil {
58 | return m.P
59 | }
60 | return nil
61 | }
62 |
63 | type SyncPlayers struct {
64 | Ps []*Player `protobuf:"bytes,1,rep,name=ps" json:"ps,omitempty"`
65 | }
66 |
67 | func (m *SyncPlayers) Reset() { *m = SyncPlayers{} }
68 | func (m *SyncPlayers) String() string { return proto.CompactTextString(m) }
69 | func (*SyncPlayers) ProtoMessage() {}
70 | func (*SyncPlayers) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
71 |
72 | func (m *SyncPlayers) GetPs() []*Player {
73 | if m != nil {
74 | return m.Ps
75 | }
76 | return nil
77 | }
78 |
79 | type Position struct {
80 | X float32 `protobuf:"fixed32,1,opt,name=X,json=x" json:"X,omitempty"`
81 | Y float32 `protobuf:"fixed32,2,opt,name=Y,json=y" json:"Y,omitempty"`
82 | Z float32 `protobuf:"fixed32,3,opt,name=Z,json=z" json:"Z,omitempty"`
83 | V float32 `protobuf:"fixed32,4,opt,name=V,json=v" json:"V,omitempty"`
84 | }
85 |
86 | func (m *Position) Reset() { *m = Position{} }
87 | func (m *Position) String() string { return proto.CompactTextString(m) }
88 | func (*Position) ProtoMessage() {}
89 | func (*Position) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
90 |
91 | type MovePackege struct {
92 | P *Position `protobuf:"bytes,1,opt,name=P,json=p" json:"P,omitempty"`
93 | ActionData int32 `protobuf:"varint,2,opt,name=ActionData,json=actionData" json:"ActionData,omitempty"`
94 | }
95 |
96 | func (m *MovePackege) Reset() { *m = MovePackege{} }
97 | func (m *MovePackege) String() string { return proto.CompactTextString(m) }
98 | func (*MovePackege) ProtoMessage() {}
99 | func (*MovePackege) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
100 |
101 | func (m *MovePackege) GetP() *Position {
102 | if m != nil {
103 | return m.P
104 | }
105 | return nil
106 | }
107 |
108 | type BroadCast struct {
109 | Pid int32 `protobuf:"varint,1,opt,name=Pid,json=pid" json:"Pid,omitempty"`
110 | Tp int32 `protobuf:"varint,2,opt,name=Tp,json=tp" json:"Tp,omitempty"`
111 | // Types that are valid to be assigned to Data:
112 | // *BroadCast_Content
113 | // *BroadCast_P
114 | // *BroadCast_ActionData
115 | Data isBroadCast_Data `protobuf_oneof:"Data"`
116 | }
117 |
118 | func (m *BroadCast) Reset() { *m = BroadCast{} }
119 | func (m *BroadCast) String() string { return proto.CompactTextString(m) }
120 | func (*BroadCast) ProtoMessage() {}
121 | func (*BroadCast) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
122 |
123 | type isBroadCast_Data interface {
124 | isBroadCast_Data()
125 | }
126 |
127 | type BroadCast_Content struct {
128 | Content string `protobuf:"bytes,3,opt,name=Content,json=content,oneof"`
129 | }
130 | type BroadCast_P struct {
131 | P *Position `protobuf:"bytes,4,opt,name=P,json=p,oneof"`
132 | }
133 | type BroadCast_ActionData struct {
134 | ActionData int32 `protobuf:"varint,5,opt,name=ActionData,json=actionData,oneof"`
135 | }
136 |
137 | func (*BroadCast_Content) isBroadCast_Data() {}
138 | func (*BroadCast_P) isBroadCast_Data() {}
139 | func (*BroadCast_ActionData) isBroadCast_Data() {}
140 |
141 | func (m *BroadCast) GetData() isBroadCast_Data {
142 | if m != nil {
143 | return m.Data
144 | }
145 | return nil
146 | }
147 |
148 | func (m *BroadCast) GetContent() string {
149 | if x, ok := m.GetData().(*BroadCast_Content); ok {
150 | return x.Content
151 | }
152 | return ""
153 | }
154 |
155 | func (m *BroadCast) GetP() *Position {
156 | if x, ok := m.GetData().(*BroadCast_P); ok {
157 | return x.P
158 | }
159 | return nil
160 | }
161 |
162 | func (m *BroadCast) GetActionData() int32 {
163 | if x, ok := m.GetData().(*BroadCast_ActionData); ok {
164 | return x.ActionData
165 | }
166 | return 0
167 | }
168 |
169 | // XXX_OneofFuncs is for the internal use of the proto package.
170 | func (*BroadCast) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) {
171 | return _BroadCast_OneofMarshaler, _BroadCast_OneofUnmarshaler, _BroadCast_OneofSizer, []interface{}{
172 | (*BroadCast_Content)(nil),
173 | (*BroadCast_P)(nil),
174 | (*BroadCast_ActionData)(nil),
175 | }
176 | }
177 |
178 | func _BroadCast_OneofMarshaler(msg proto.Message, b *proto.Buffer) error {
179 | m := msg.(*BroadCast)
180 | // Data
181 | switch x := m.Data.(type) {
182 | case *BroadCast_Content:
183 | b.EncodeVarint(3<<3 | proto.WireBytes)
184 | b.EncodeStringBytes(x.Content)
185 | case *BroadCast_P:
186 | b.EncodeVarint(4<<3 | proto.WireBytes)
187 | if err := b.EncodeMessage(x.P); err != nil {
188 | return err
189 | }
190 | case *BroadCast_ActionData:
191 | b.EncodeVarint(5<<3 | proto.WireVarint)
192 | b.EncodeVarint(uint64(x.ActionData))
193 | case nil:
194 | default:
195 | return fmt.Errorf("BroadCast.Data has unexpected type %T", x)
196 | }
197 | return nil
198 | }
199 |
200 | func _BroadCast_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) {
201 | m := msg.(*BroadCast)
202 | switch tag {
203 | case 3: // Data.Content
204 | if wire != proto.WireBytes {
205 | return true, proto.ErrInternalBadWireType
206 | }
207 | x, err := b.DecodeStringBytes()
208 | m.Data = &BroadCast_Content{x}
209 | return true, err
210 | case 4: // Data.P
211 | if wire != proto.WireBytes {
212 | return true, proto.ErrInternalBadWireType
213 | }
214 | msg := new(Position)
215 | err := b.DecodeMessage(msg)
216 | m.Data = &BroadCast_P{msg}
217 | return true, err
218 | case 5: // Data.ActionData
219 | if wire != proto.WireVarint {
220 | return true, proto.ErrInternalBadWireType
221 | }
222 | x, err := b.DecodeVarint()
223 | m.Data = &BroadCast_ActionData{int32(x)}
224 | return true, err
225 | default:
226 | return false, nil
227 | }
228 | }
229 |
230 | func _BroadCast_OneofSizer(msg proto.Message) (n int) {
231 | m := msg.(*BroadCast)
232 | // Data
233 | switch x := m.Data.(type) {
234 | case *BroadCast_Content:
235 | n += proto.SizeVarint(3<<3 | proto.WireBytes)
236 | n += proto.SizeVarint(uint64(len(x.Content)))
237 | n += len(x.Content)
238 | case *BroadCast_P:
239 | s := proto.Size(x.P)
240 | n += proto.SizeVarint(4<<3 | proto.WireBytes)
241 | n += proto.SizeVarint(uint64(s))
242 | n += s
243 | case *BroadCast_ActionData:
244 | n += proto.SizeVarint(5<<3 | proto.WireVarint)
245 | n += proto.SizeVarint(uint64(x.ActionData))
246 | case nil:
247 | default:
248 | panic(fmt.Sprintf("proto: unexpected type %T in oneof", x))
249 | }
250 | return n
251 | }
252 |
253 | type Talk struct {
254 | Content string `protobuf:"bytes,1,opt,name=Content,json=content" json:"Content,omitempty"`
255 | }
256 |
257 | func (m *Talk) Reset() { *m = Talk{} }
258 | func (m *Talk) String() string { return proto.CompactTextString(m) }
259 | func (*Talk) ProtoMessage() {}
260 | func (*Talk) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
261 |
262 | func init() {
263 | proto.RegisterType((*SyncPid)(nil), "pb.SyncPid")
264 | proto.RegisterType((*Player)(nil), "pb.Player")
265 | proto.RegisterType((*SyncPlayers)(nil), "pb.SyncPlayers")
266 | proto.RegisterType((*Position)(nil), "pb.Position")
267 | proto.RegisterType((*MovePackege)(nil), "pb.MovePackege")
268 | proto.RegisterType((*BroadCast)(nil), "pb.BroadCast")
269 | proto.RegisterType((*Talk)(nil), "pb.Talk")
270 | }
271 |
272 | func init() { proto.RegisterFile("msg.proto", fileDescriptor0) }
273 |
274 | var fileDescriptor0 = []byte{
275 | // 303 bytes of a gzipped FileDescriptorProto
276 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x6c, 0x91, 0x4d, 0x4b, 0xc3, 0x30,
277 | 0x18, 0xc7, 0x4d, 0xda, 0xae, 0xf6, 0xe9, 0x10, 0xc9, 0xa9, 0xa8, 0x48, 0xc9, 0x49, 0x2f, 0x3d,
278 | 0x28, 0x78, 0xb7, 0xf3, 0xa0, 0x07, 0x21, 0xc4, 0x21, 0xea, 0x2d, 0x7d, 0x61, 0x94, 0xcd, 0x26,
279 | 0xb4, 0x61, 0x58, 0xbf, 0x85, 0x5f, 0xc3, 0x4f, 0x69, 0x92, 0xea, 0x54, 0xb6, 0x53, 0xf9, 0x3d,
280 | 0x6f, 0xff, 0x5f, 0x09, 0x44, 0xaf, 0xfd, 0x22, 0x53, 0x9d, 0xd4, 0x92, 0x60, 0x55, 0xd0, 0x63,
281 | 0x08, 0x1f, 0x86, 0xb6, 0x64, 0x4d, 0x45, 0x0e, 0xc1, 0x33, 0x9f, 0x04, 0xa5, 0xe8, 0x2c, 0xe0,
282 | 0x9e, 0x6a, 0x2a, 0x7a, 0x05, 0x13, 0xb6, 0x12, 0x43, 0xdd, 0x6d, 0xf7, 0xc8, 0x11, 0x20, 0x96,
283 | 0x60, 0xc3, 0xf1, 0xc5, 0x34, 0x53, 0x45, 0xc6, 0x64, 0xdf, 0xe8, 0x46, 0xb6, 0x1c, 0x29, 0x7a,
284 | 0x0e, 0xb1, 0x3b, 0xea, 0x76, 0x7b, 0x33, 0x8a, 0x55, 0x6f, 0x76, 0x3d, 0x33, 0x0b, 0x6e, 0xd6,
285 | 0x35, 0xb8, 0xa9, 0xd2, 0x1c, 0xf6, 0x7f, 0x36, 0xc9, 0x14, 0xd0, 0x93, 0x8b, 0xc0, 0x1c, 0xbd,
286 | 0x59, 0x7a, 0x76, 0x01, 0x86, 0x06, 0x4b, 0x2f, 0x89, 0x37, 0xd2, 0xbb, 0xa5, 0xc7, 0xc4, 0x1f,
287 | 0x69, 0x4d, 0xef, 0x20, 0xbe, 0x97, 0xeb, 0x9a, 0x89, 0x72, 0x59, 0x2f, 0xea, 0xd1, 0x0c, 0xed,
288 | 0x34, 0x23, 0xa7, 0x00, 0xd7, 0xa5, 0x85, 0x1b, 0xa1, 0x85, 0xbb, 0x1e, 0x70, 0x10, 0x9b, 0x0a,
289 | 0xfd, 0x40, 0x10, 0xe5, 0x9d, 0x14, 0xd5, 0x4c, 0xf4, 0x7a, 0xc7, 0x5f, 0x1f, 0x00, 0x9e, 0xab,
290 | 0xef, 0x3d, 0xac, 0x95, 0xc9, 0x0a, 0x67, 0xb2, 0xd5, 0x75, 0xab, 0x9d, 0x5c, 0x74, 0xbb, 0xc7,
291 | 0xc3, 0x72, 0x2c, 0x90, 0x13, 0xeb, 0xe1, 0x6f, 0x7b, 0x98, 0x19, 0x63, 0x92, 0xfe, 0x33, 0x09,
292 | 0xec, 0x45, 0xd3, 0xf8, 0xe3, 0x92, 0x4f, 0xc0, 0x77, 0x4e, 0x29, 0xf8, 0x73, 0xb1, 0x5a, 0x92,
293 | 0xe4, 0x37, 0xcb, 0x1a, 0x45, 0x9b, 0xa4, 0x3c, 0xf8, 0xc4, 0x98, 0x15, 0xc5, 0xc4, 0x3d, 0xeb,
294 | 0xe5, 0x57, 0x00, 0x00, 0x00, 0xff, 0xff, 0x50, 0x88, 0x10, 0xb8, 0xe3, 0x01, 0x00, 0x00,
295 | }
296 |
--------------------------------------------------------------------------------