├── GateApp ├── .gitignore ├── doc.go ├── main.go ├── PrivateMsg.go ├── GateMsgFilter.go └── GateLogic.go ├── LoginApp ├── .gitignore ├── doc.go ├── PrivateMsg.go ├── main.go ├── LoginMsgFilter.go ├── LoginDBLogic.go └── LoginMainLogic.go ├── RouterApp ├── .gitignore ├── doc.go ├── main.go ├── PrivateMsg.go ├── RouterMsgFilter.go └── RouterLogic.go ├── ClientForTest ├── .gitignore ├── doc.go └── main.go ├── .gitignore ├── bs_public ├── doc.go └── bs_public.go ├── go.mod ├── LICENSE ├── go.sum └── README.md /GateApp/.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | -------------------------------------------------------------------------------- /LoginApp/.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | -------------------------------------------------------------------------------- /RouterApp/.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | -------------------------------------------------------------------------------- /ClientForTest/.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | /src/RouterApp/*.exe 3 | src 4 | pkg 5 | -------------------------------------------------------------------------------- /LoginApp/doc.go: -------------------------------------------------------------------------------- 1 | // LoginApp project doc.go 2 | 3 | /* 4 | LoginApp document 5 | */ 6 | package main 7 | -------------------------------------------------------------------------------- /RouterApp/doc.go: -------------------------------------------------------------------------------- 1 | // RouterApp project doc.go 2 | 3 | /* 4 | RouterApp document 5 | */ 6 | package main 7 | -------------------------------------------------------------------------------- /bs_public/doc.go: -------------------------------------------------------------------------------- 1 | // bs_public project doc.go 2 | 3 | /* 4 | bs_public document 5 | */ 6 | package bs_public 7 | -------------------------------------------------------------------------------- /ClientForTest/doc.go: -------------------------------------------------------------------------------- 1 | // ClientForTest project doc.go 2 | 3 | /* 4 | ClientForTest document 5 | 客户端测试程序 6 | */ 7 | package main 8 | -------------------------------------------------------------------------------- /bs_public/bs_public.go: -------------------------------------------------------------------------------- 1 | // public project public.go 2 | package bs_public 3 | 4 | type LogicProcess interface { 5 | Init(my_pool *Single) bool 6 | } 7 | -------------------------------------------------------------------------------- /GateApp/doc.go: -------------------------------------------------------------------------------- 1 | // GateApp project doc.go 2 | 3 | /* 4 | GateApp document 5 | */ 6 | package main 7 | 8 | //断开连接 9 | //func (this *GateLogic) Network_OnConnClose(req *protodf.TCPSessionClose) 10 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/3zheng/railgun 2 | 3 | go 1.23.0 4 | 5 | require ( 6 | github.com/3zheng/railcommon v0.0.2 7 | github.com/3zheng/railproto v0.0.3 8 | google.golang.org/protobuf v1.35.1 9 | ) 10 | 11 | //replace github.com/3zheng/railgun/common v0.0.2 => ./common 12 | 13 | require ( 14 | filippo.io/edwards25519 v1.1.0 // indirect 15 | github.com/go-sql-driver/mysql v1.8.1 // indirect 16 | ) 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) <2017> 2 | 3 |   Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 |    5 |   The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /RouterApp/main.go: -------------------------------------------------------------------------------- 1 | // RouterApp project main.go 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | 7 | "github.com/3zheng/railcommon" 8 | protodf "github.com/3zheng/railproto" 9 | ) 10 | 11 | func CreateRouterLogicInstance() *RouterLogic { 12 | return new(RouterLogic) 13 | } 14 | 15 | func main() { 16 | quit := make(chan int) 17 | var myAppId uint32 = 50 18 | pNetAgent := railcommon.CreateNetAgent("0.0.0.0:2001") //监听2001端口 19 | pLogicPool := railcommon.CreateMsgPool(quit, uint32(protodf.EnumAppType_Router), myAppId) 20 | pRouterLogic := CreateRouterLogicInstance() 21 | //将他们都与Pool绑定起来 22 | pLogicPool.AddLogicProcess(pRouterLogic) 23 | pLogicPool.BindNetAgent(pNetAgent) 24 | //运行 25 | ok := pLogicPool.InitAndRun(nil) 26 | if ok { 27 | fmt.Println("初始化完毕,监听2001端口") 28 | pInitMsg := &PrivateInitMsg{ 29 | pNetAgent: pNetAgent, 30 | pRouterAgent: nil, //本身就是router不需要连接router的RouterAgent 31 | myAppId: myAppId} 32 | //在初始化完毕后向逻辑层发送初始化报文,带着一些初始化信息,比如自己的APPID等 33 | pLogicPool.PushMsg(pInitMsg, 0) 34 | } 35 | //阻塞直到收到quit请求 36 | for { 37 | select { 38 | case v := <-quit: 39 | if v == 1 { //只有在收到1时才退出主线程 40 | return 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= 2 | filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= 3 | github.com/3zheng/railcommon v0.0.2 h1:/sY+jaHH31vKXibJFr08o7mLw0vfrclv9RxY0QVIKqM= 4 | github.com/3zheng/railcommon v0.0.2/go.mod h1:aqyqqQ0R9wzDJTzf1QMEJWQhmjCU1BbI2MtMnqPfcWM= 5 | github.com/3zheng/railproto v0.0.3 h1:L+HDOjthUH2o4npD6oofKolvIabjxPvy5yVUhibMzOA= 6 | github.com/3zheng/railproto v0.0.3/go.mod h1:N9fbCxfaYIyFnJuG5+17/h8CQgcwIjnTQOPnT4brehc= 7 | github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= 8 | github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= 9 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= 10 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 11 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 12 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 13 | google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= 14 | google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= 15 | -------------------------------------------------------------------------------- /GateApp/main.go: -------------------------------------------------------------------------------- 1 | // GateApp project main.go 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | 7 | "github.com/3zheng/railcommon" 8 | protodf "github.com/3zheng/railproto" 9 | ) 10 | 11 | func CreateGateLogicInstance() *GateLogic { 12 | return new(GateLogic) 13 | } 14 | 15 | func main() { 16 | //先创建需要的变量 17 | quit := make(chan int) 18 | var myAppId uint32 = 101 19 | pNetAgent := railcommon.CreateNetAgent("0.0.0.0:4101") //监听4101端口 20 | pLogicPool := railcommon.CreateMsgPool(quit, uint32(protodf.EnumAppType_Gate), myAppId) 21 | pRouterAgent := railcommon.CreateRouterAgent("127.0.0.1:2001") //连接127.0.0.1:2001地址 22 | pGateLogic := CreateGateLogicInstance() 23 | //将他们都与Pool绑定起来 24 | pLogicPool.AddLogicProcess(pGateLogic) 25 | pLogicPool.BindNetAgent(pNetAgent) 26 | pLogicPool.BindRouterAgent(pRouterAgent) 27 | //运行 28 | ok := pLogicPool.InitAndRun(nil) 29 | if ok { 30 | fmt.Println("初始化完毕") 31 | pInitMsg := &PrivateInitMsg{ 32 | pNetAgent: pNetAgent, 33 | pRouterAgent: pRouterAgent, 34 | myAppId: myAppId} 35 | //在初始化完毕后向逻辑层发送初始化报文,带着一些初始化信息,比如自己的APPID等 36 | pLogicPool.PushMsg(pInitMsg, 0) 37 | } 38 | 39 | //阻塞直到收到quit请求 40 | for { 41 | select { 42 | case v := <-quit: 43 | if v == 1 { //只有在收到1时才退出主线程 44 | return 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /GateApp/PrivateMsg.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/3zheng/railcommon" 5 | 6 | proto "google.golang.org/protobuf/proto" 7 | "google.golang.org/protobuf/reflect/protoreflect" 8 | ) 9 | 10 | /*这个源文件的作用: 11 | //自己构建用来gate内部使用的私有报文 12 | //为了能在pool传输必须继承proto.Message,所以要重写以下3个函数 13 | //Reset() 14 | //String() string 15 | //ProtoMessage() 16 | //但是由于不是由protoc.exe来产生的正常报文,是无法被proto解析的 17 | */ 18 | 19 | // 初始化报文 20 | type PrivateInitMsg struct { 21 | pNetAgent *railcommon.NetAgent 22 | pRouterAgent *railcommon.RouterAgent 23 | myAppId uint32 24 | } 25 | 26 | func (*PrivateInitMsg) ProtoReflect() protoreflect.Message { 27 | return nil 28 | } 29 | 30 | // 均为空 31 | func (*PrivateInitMsg) Reset() { 32 | 33 | } 34 | 35 | func (*PrivateInitMsg) String() string { 36 | return "PrivateInitMsg" 37 | } 38 | 39 | func (*PrivateInitMsg) ProtoMessage() { 40 | 41 | } 42 | 43 | // 当逻辑层需要区别这个报文是自己延时推送给自己的报文还是其他人推送过来的报文的时候, 44 | // 可以用这个私有类型,pDelay用来装延时发送的实际报文指针,DelayTime是延时时间 45 | // 当收到这个报文时,说明是自己向自己延时推送的 46 | type PrivateDelayMsg struct { 47 | pDelay proto.Message //延时发送的报文 48 | DelayTime uint64 //延时发送的时间 49 | } 50 | 51 | // 均为空 52 | func (*PrivateDelayMsg) Reset() { 53 | 54 | } 55 | 56 | func (*PrivateDelayMsg) String() string { 57 | return "PrivateDelayMsg" 58 | } 59 | 60 | func (*PrivateDelayMsg) ProtoMessage() { 61 | 62 | } 63 | -------------------------------------------------------------------------------- /RouterApp/PrivateMsg.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/3zheng/railcommon" 5 | proto "google.golang.org/protobuf/proto" 6 | "google.golang.org/protobuf/reflect/protoreflect" 7 | ) 8 | 9 | /*这个源文件的作用: 10 | //自己构建用来router内部使用的私有报文 11 | //为了能在pool传输必须继承proto.Message,所以要重写以下3个函数 12 | //Reset() 13 | //String() string 14 | //ProtoMessage() 15 | //但是由于不是由protoc.exe来产生的正常报文,是无法被proto解析的 16 | */ 17 | 18 | // 初始化报文 19 | type PrivateInitMsg struct { 20 | pNetAgent *railcommon.NetAgent 21 | pRouterAgent *railcommon.RouterAgent 22 | myAppId uint32 23 | } 24 | 25 | // 均为空 26 | func (*PrivateInitMsg) ProtoReflect() protoreflect.Message { 27 | return nil 28 | } 29 | 30 | func (*PrivateInitMsg) Reset() { 31 | 32 | } 33 | 34 | func (*PrivateInitMsg) String() string { 35 | return "PrivateInitMsg" 36 | } 37 | 38 | func (*PrivateInitMsg) ProtoMessage() { 39 | 40 | } 41 | 42 | // 当逻辑层需要区别这个报文是自己延时推送给自己的报文还是其他人推送过来的报文的时候, 43 | // 可以用这个私有类型,pDelay用来装延时发送的实际报文指针,DelayTime是延时时间 44 | // 当收到这个报文时,说明是自己向自己延时推送的 45 | type PrivateDelayMsg struct { 46 | pDelay proto.Message //延时发送的报文 47 | DelayTime uint64 //延时发送的时间 48 | } 49 | 50 | // 均为空 51 | func (*PrivateDelayMsg) Reset() { 52 | 53 | } 54 | 55 | func (*PrivateDelayMsg) String() string { 56 | return "PrivateDelayMsg" 57 | } 58 | 59 | func (*PrivateDelayMsg) ProtoMessage() { 60 | 61 | } 62 | -------------------------------------------------------------------------------- /LoginApp/PrivateMsg.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/3zheng/railcommon" 5 | proto "google.golang.org/protobuf/proto" 6 | "google.golang.org/protobuf/reflect/protoreflect" 7 | ) 8 | 9 | /*这个源文件的作用: 10 | //自己构建用来login内部使用的私有报文 11 | //为了能在pool传输必须继承proto.Message,所以要重写以下3个函数 12 | //Reset() 13 | //String() string 14 | //ProtoMessage() 15 | //但是由于不是由protoc.exe来产生的正常报文,是无法被proto解析的 16 | */ 17 | 18 | // 初始化报文 19 | type PrivateInitMsg struct { 20 | pNetAgent *railcommon.NetAgent 21 | pRouterAgent *railcommon.RouterAgent 22 | pMainPool *railcommon.SingleMsgPool 23 | pDBPool *railcommon.SingleMsgPool 24 | myAppId uint32 25 | } 26 | 27 | // 均为空 28 | func (*PrivateInitMsg) ProtoReflect() protoreflect.Message { 29 | return nil 30 | } 31 | 32 | func (*PrivateInitMsg) Reset() { 33 | 34 | } 35 | 36 | func (*PrivateInitMsg) String() string { 37 | return "PrivateInitMsg" 38 | } 39 | 40 | func (*PrivateInitMsg) ProtoMessage() { 41 | 42 | } 43 | 44 | // 当逻辑层需要区别这个报文是自己延时推送给自己的报文还是其他人推送过来的报文的时候, 45 | // 可以用这个私有类型,pDelay用来装延时发送的实际报文指针,DelayTime是延时时间 46 | // 当收到这个报文时,说明是自己向自己延时推送的 47 | type PrivateDelayMsg struct { 48 | pDelay proto.Message //延时发送的报文 49 | DelayTime uint64 //延时发送的时间 50 | } 51 | 52 | // 均为空 53 | func (*PrivateDelayMsg) Reset() { 54 | 55 | } 56 | 57 | func (*PrivateDelayMsg) String() string { 58 | return "PrivateDelayMsg" 59 | } 60 | 61 | func (*PrivateDelayMsg) ProtoMessage() { 62 | 63 | } 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | use MIT license:就是你想干啥就干啥的开源协议 2 | 3 | # railgun 4 | 一个简单的CS模式的游戏服务器框架,引入protobuf第三方库作为报文协议,mysql作为数据库,Go-mysql-driver作为驱动库。这三个东西是需要自己另外安装 5 | 6 | ## 部署和编译 7 | go build ./GateApp 8 | go build ./RouterApp 9 | go build ./LoginApp 10 | 11 | go build ./ClientForTest 12 | 13 | 编译: 14 | GateApp,RouterApp,LoginApp是package main的exe工程,所以可以单独拿出来放到$GOPATH目录下,不会影响编译 15 | 16 | ## 1.服务器架构 17 | 参考链接 http://blog.csdn.NET/easy_mind/article/details/53321919 18 | 19 | ## 2.单个服务器APP结构 20 | 参考链接 http://blog.csdn.Net/easy_mind/article/details/53322216 21 | 22 | ## 3.报文层级 23 | 参考链接 http://blog.csdn.net/easy_mind/article/details/53322280 24 | 25 | ## 4.通过代码来简单说明 26 | 参考链接 http://blog.csdn.net/easy_mind/article/details/53322300 27 | 28 | ## 5.目前存在的不足和后续可能的工作展开 29 | 参考链接 http://blog.csdn.net/easy_mind/article/details/53322687 30 | 31 | ## 6.如何应用简述 32 | 每当根据业务需求新写一个业务App时,需要手写的源文件有 33 | package main的main.go、PrivateMsg.go(这个如果没有需要新增私有报文就直接复制过来就行了)、XXXMsgFilter.go、XXXMainLogic.go、XXXDBLogic.go(如果需要操作数据库的话) 34 | 35 | Package protodf的SetBaseInfo.go中的SetBaseKindAndSubId函数,要根据proto数据类型对其new Base并对Base.KindId和SubId赋值 36 | 37 | ## 7.个人的吐槽 38 | 由于不知道怎么排版README,所以项目就在这里简述一下,详细文档说明移步到我的blog。个人水平有限,希望抛砖引玉吸引大神or练手的同学来将整个框架更加完善,共同为独立游戏开发者这个群体尽一点绵薄之力。希望感兴趣的同学or大神能和我一起完善开发,如果在征求您同意的前提下,我会将您的名字放入下面的贡献者名单中。我目前在369793160这个群里面,里面很多golang的高手,如果要共同学习进步可以加这个群。不过我不是群主也不是管理员,是否能加的进来看运气了,O(∩_∩)O哈哈~。如果真的用的人挺多的,那我届时会再建一个群,当前有问题可以发邮件到914509007@qq.com给我或者加我QQ,我有空都会看的 39 | 40 | 正常画风:如果项目对你有帮助,请点个☆,你的随手点赞是我继续改进的动力。 41 | 恶意卖萌画风:给人家点个☆嘛,点一下又不会怀孕(;¬_¬) 没粉丝就没动力,没时间。全部都是时辰的错 42 | 43 | 文档连接:http://blog.csdn.net/easy_mind/article/details/53260574 44 | PS:如果文档或者参考链接打不开的话,可能是我刚修改了文档,CSDN还在审核,需要审核过了才会重新显示。将来可能会放在studygolang.com这里,这里好像不审核。 45 | 46 | 贡献者名单: 47 | 中二病也要当码畜 email:914509007@qq.com 48 | 49 | PS:项目名源于《某科学的超电磁炮》,自曝死宅属性,233333。另外我要对我的死宅同胞们膜一句:当年花前月下的时候叫人家美琴酱(炮姐),如今新人胜旧人就叫人家上条夫人(or电磁炮)。你们呀就是有点好,出了新番比西方记者跑的都快,叫起“老婆”来一点都不含糊,实在是too young, too simple,sometimes naive。暗牧安谷瑞,不说了,我去丢我蕾姆去了。23333,逃 ε=ε=ε=(~ ̄▽ ̄)~ -------------------------------------------------------------------------------- /LoginApp/main.go: -------------------------------------------------------------------------------- 1 | // LoginApp project main.go 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | 7 | "github.com/3zheng/railcommon" 8 | protodf "github.com/3zheng/railproto" 9 | ) 10 | 11 | func CreateLoginLogicInstance() *LoginMainLogic { 12 | return new(LoginMainLogic) 13 | } 14 | 15 | func CreateLoginDBInstance() *LoginDBLogic { 16 | return new(LoginDBLogic) 17 | } 18 | 19 | func main() { 20 | //先创建需要的变量 21 | quit := make(chan int) 22 | var myAppId uint32 = 30 23 | pLogicPool := railcommon.CreateMsgPool(quit, uint32(protodf.EnumAppType_Login), myAppId) 24 | pRouterAgent := railcommon.CreateRouterAgent("127.0.0.1:2001") //连接127.0.0.1:2001地址 25 | pMainLoginLogic := CreateLoginLogicInstance() 26 | //将他们都与Pool绑定起来 27 | pLogicPool.AddLogicProcess(pMainLoginLogic) 28 | pLogicPool.BindRouterAgent(pRouterAgent) 29 | //创建数据库协程,只有主线程pLogicPool需要绑定RouterAgent,数据库协程是不需要的,所以退出quit也不需要创建,因为程序退出又逻辑主线程来控制 30 | //先创建需要的变量 31 | pDBPool := railcommon.CreateMsgPool(nil, uint32(protodf.EnumAppType_Login), myAppId) 32 | for i := 0; i < 10; i++ { 33 | //创建10个数据库协程,因为数据库IO速度比较慢会存在阻塞时间,所以要多开几个, 34 | //数据库逻辑协程间最好不要有数据通信,都应该通过主逻辑POOL与主逻辑进行通信 35 | //相对的主逻辑最好只用一个协程,这样写业务逻辑也好写, 36 | pDBLogic := CreateLoginDBInstance() 37 | pDBProcess := railcommon.CreateADODatabase("root:123456@tcp(localhost:3306)/gotest?charset=utf8") 38 | //将他们都与Pool绑定起来 39 | pDBPool.AddLogicProcess(pDBLogic) 40 | pDBPool.AddDataBaseProcess(pDBProcess) 41 | } 42 | 43 | //运行主逻辑线程pool 44 | ok := pLogicPool.InitAndRun(nil) 45 | if ok { 46 | fmt.Println("主逻辑POOL初始化完毕") 47 | //这里要在初始化完毕后把DBPool和MainPool的指针传过去,这样才能两个Pool之间才可以相互传递数据 48 | pInitMsg := &PrivateInitMsg{ 49 | pNetAgent: nil, 50 | pRouterAgent: pRouterAgent, 51 | myAppId: myAppId, 52 | pMainPool: pLogicPool, 53 | pDBPool: pDBPool} 54 | //在初始化完毕后向逻辑层发送初始化报文,带着一些初始化信息,比如自己的APPID等 55 | pLogicPool.PushMsg(pInitMsg, 0) 56 | } else { 57 | return 58 | } 59 | //运行数据库协程pool 60 | pInitMsg := &PrivateInitMsg{ 61 | pNetAgent: nil, 62 | pRouterAgent: pRouterAgent, 63 | myAppId: myAppId, 64 | pMainPool: pLogicPool, 65 | pDBPool: pDBPool} 66 | ok = pDBPool.InitAndRun(pInitMsg) 67 | if ok { 68 | fmt.Println("数据库逻辑POOL初始化完毕") 69 | } else { 70 | return 71 | } 72 | //阻塞直到收到quit请求 73 | for { 74 | select { 75 | case v := <-quit: 76 | if v == 1 { //只有在收到1时才退出主线程 77 | return 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /LoginApp/LoginMsgFilter.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | protodf "github.com/3zheng/railproto" 7 | proto "google.golang.org/protobuf/proto" 8 | ) 9 | 10 | //login消息过滤器,用于一般消息和protodf.RouterTransferData消息的相互转换 11 | 12 | // 这个函数处理来自pool的消息,因为pool里面的消息不止来源于routeragent层,也可能来源于其他逻辑层或者自身的延时发送消息 13 | // 所以,这个函数里的消息不止有RouterTransferData,但是只需要处理RouterTransferData这个消息就行了 14 | // 把从RouterTransferData解析出来的login应该处理的报文返回,不处理的报文直接丢弃返回nil 15 | func Login_CreateCommonMsgByRouterTransferData(req proto.Message) proto.Message { 16 | switch data := req.(type) { 17 | case *protodf.RouterTransferData: 18 | switch data.DataCmdKind { //判断大类 19 | case uint32(protodf.CMDKindId_IDKindClient): 20 | switch data.DataCmdSubid { //判断小类 21 | case uint32(protodf.CMDID_Client_IDLoginReq): 22 | msg := new(protodf.LoginReq) 23 | err := proto.Unmarshal(data.Data, msg) 24 | if err != nil { 25 | fmt.Println("解析LoginReq出错") 26 | return nil 27 | } 28 | protodf.SetBaseKindAndSubId(msg) 29 | protodf.CopyBaseExceptKindAndSubId(msg.Base, data.Base) 30 | msg.Base.AttAppid = data.SrcAppid 31 | msg.Base.AttApptype = data.SrcApptype 32 | msg.Base.RemoteAdd = data.ClientRemoteAddress 33 | msg.Base.GateConnId = data.AttGateconnid 34 | return msg 35 | default: 36 | fmt.Println("不识别的client报文,DataSubId=", data.DataCmdSubid) 37 | return nil //丢弃这个RouterTransferData报文 38 | } 39 | default: //protodf.TCPTransferMsg报文的成员变量大类DataKindId不为CMDKindId_IDKindGate都丢弃 40 | return nil //丢弃这个TCPTransferMsg报文 41 | } 42 | default: //*protodf.RouterTransferData以外的报文直接返回 43 | return req 44 | } 45 | return nil 46 | } 47 | 48 | // 这个函数处理发往pool的消息,因为除了普通消息以外还有kick掉一个session这样的消息 49 | // 所以,这个函数里的要把kick消息区分出来,其他消息则转化成TCPTransferMsg 50 | func Login_CreateRouterTransferDataByCommonMsg(req proto.Message, pBase *protodf.BaseInfo) proto.Message { 51 | switch data := req.(type) { 52 | case *protodf.TCPTransferMsg: //实际上不应该传TCPTransferMsg类型的报文到这个函数里来 53 | return data 54 | case *protodf.RouterTransferData: //实际上不应该传RouterTransferData类型的报文到这个函数里来 55 | return data 56 | default: 57 | msg := new(protodf.RouterTransferData) 58 | protodf.SetBaseKindAndSubId(msg) 59 | protodf.CopyBaseExceptKindAndSubId(msg.Base, pBase) 60 | msg.DataCmdKind = pBase.KindId 61 | msg.DataCmdSubid = pBase.SubId 62 | msg.DestAppid = pBase.AttAppid 63 | msg.DestApptype = pBase.AttApptype 64 | msg.AttGateconnid = pBase.GateConnId 65 | buf, err := proto.Marshal(req) 66 | if err != nil { 67 | return nil 68 | } else { 69 | msg.Data = buf 70 | return msg 71 | } 72 | } 73 | return req 74 | } 75 | -------------------------------------------------------------------------------- /LoginApp/LoginDBLogic.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/3zheng/railcommon" 7 | protodf "github.com/3zheng/railproto" 8 | proto "google.golang.org/protobuf/proto" 9 | ) 10 | 11 | type LoginDBLogic struct { 12 | mLogicPool *railcommon.SingleMsgPool //主逻辑线程的Pool 13 | mDBPool *railcommon.SingleMsgPool //自身绑定的SingleMsgPool 14 | mRouterAgents *railcommon.RouterAgent //暂时一个router agent,以后可能会有多个 15 | mMyAppid uint32 16 | } 17 | 18 | // 实现railcommon.ILogicProcess的三个接口函数 19 | func (this *LoginDBLogic) Init(myPool *railcommon.SingleMsgPool) bool { 20 | this.mDBPool = myPool 21 | return true 22 | } 23 | 24 | func (this *LoginDBLogic) ProcessReq(req proto.Message, pDatabase *railcommon.CADODatabase) { 25 | //因为DBPool的报文来源都是主逻辑Pool,并没有直接绑定routerAgent,所以收到的全部都是普通报文。不需要调用Login_CreateCommonMsgByRouterTransferData 26 | switch data := req.(type) { 27 | case *PrivateInitMsg: 28 | this.Private_OnInit(data) 29 | case *protodf.LoginReq: 30 | this.Client_OnDBLoginReq(data, pDatabase) 31 | default: 32 | return 33 | } 34 | } 35 | 36 | func (this *LoginDBLogic) OnPulse(ms uint64) { 37 | //定时调用程序 38 | } 39 | 40 | func (this *LoginDBLogic) Private_OnInit(req *PrivateInitMsg) { 41 | this.mMyAppid = req.myAppId 42 | this.mLogicPool = req.pMainPool 43 | } 44 | 45 | func (this *LoginDBLogic) Client_OnDBLoginReq(req *protodf.LoginReq, pDatabase *railcommon.CADODatabase) { 46 | sqlExpress := fmt.Sprintf("select * from user_base where login_account = '%s' and passwd ='%s'", req.LoginAccount, req.LoginPassword) 47 | pDatabase.ReadFromDB(sqlExpress) 48 | 49 | rsp := new(protodf.LoginRsp) 50 | protodf.SetBaseKindAndSubId(rsp) 51 | protodf.CopyBaseExceptKindAndSubId(rsp.Base, req.Base) 52 | rsp.UserSesionInfo.Client_IP = req.Base.RemoteAdd 53 | protodf.OutputMyLog("login RemoteAdd=", req.Base.RemoteAdd) 54 | if pDatabase.ReadInfo.RowNum != 0 { 55 | var userId uint64 56 | var nickName string 57 | pDatabase.GetValueByRowIdAndColName(0, "userid", &userId) 58 | rsp.UserBaseInfo.UserId = userId 59 | rsp.UserId = userId 60 | pDatabase.GetValueByRowIdAndColName(0, "nick_name", &nickName) 61 | rsp.UserBaseInfo.NickName = nickName 62 | rsp.LoginResult = protodf.LoginRsp_SUCCESS //登录成功 63 | fmt.Println("登录成功,userId=", userId) 64 | } else { 65 | fmt.Println("返回行数为0,密码或者账号错误") 66 | rsp.LoginResult = protodf.LoginRsp_FALSEPW 67 | } 68 | //把登录回复传给主逻辑线程处理 69 | this.PushToMainPool(rsp) 70 | } 71 | 72 | // 向DBPOOL发送(伪),这个发送实际上不走TCP/IP,是程序内部间的“发送” 73 | func (this *LoginDBLogic) PushToMainPool(req proto.Message) { 74 | if this.mLogicPool != nil { 75 | this.mLogicPool.PushMsg(req, 0) //往mDBPool的队列尾推入消息 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /RouterApp/RouterMsgFilter.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | protodf "github.com/3zheng/railproto" 7 | proto "google.golang.org/protobuf/proto" 8 | ) 9 | 10 | //gate消息过滤器,用于一般消息和protodf.TCPTransferMsg消息的相互转换 11 | 12 | // 这个函数处理来自pool的消息,因为pool里面的消息不止来源于net层,也可能来源于其他逻辑层或者自身的延时发送消息 13 | // 所以,这个函数里的消息不止有TCPTransferMsg,但是只需要处理TCPTransferMsg这个消息就行了 14 | // 把从TCPTransferMsg解析出来的gate应该处理的报文返回,不处理的报文直接丢弃返回nil 15 | func Router_CreateCommonMsgByTCPTransferMsg(req proto.Message) proto.Message { 16 | switch data := req.(type) { 17 | case *protodf.TCPTransferMsg: 18 | switch data.DataKindId { //判断大类 19 | case uint32(protodf.CMDKindId_IDKindRouter): 20 | switch data.DataSubId { //判断小类 21 | case uint32(protodf.CMDID_Router_IDTransferDataRt): 22 | msg := new(protodf.RouterTransferData) 23 | err := proto.Unmarshal(data.Data, msg) 24 | protodf.SetBaseKindAndSubId(msg) 25 | protodf.CopyBaseExceptKindAndSubId(msg.Base, data.Base) 26 | if err != nil { 27 | fmt.Println("解析PulseReq出错") 28 | return nil 29 | } 30 | return msg 31 | case uint32(protodf.CMDID_Router_IDRegisterAppReq): 32 | msg := new(protodf.RegisterAppReq) 33 | err := proto.Unmarshal(data.Data, msg) 34 | protodf.SetBaseKindAndSubId(msg) 35 | protodf.CopyBaseExceptKindAndSubId(msg.Base, data.Base) 36 | if err != nil { 37 | fmt.Println("解析PulseReq出错") 38 | return nil 39 | } 40 | return msg 41 | case uint32(protodf.CMDID_Router_IDRegisterAppRsp): 42 | msg := new(protodf.RegisterAppRsp) 43 | err := proto.Unmarshal(data.Data, msg) 44 | protodf.SetBaseKindAndSubId(msg) 45 | protodf.CopyBaseExceptKindAndSubId(msg.Base, data.Base) 46 | if err != nil { 47 | fmt.Println("解析PulseReq出错") 48 | return nil 49 | } 50 | return msg 51 | default: 52 | fmt.Println("不识别的gate报文,DataSubId=", data.DataSubId) 53 | return nil //丢弃这个TCPTransferMsg报文 54 | } 55 | default: //protodf.TCPTransferMsg报文的成员变量大类DataKindId不为CMDKindId_IDKindGate都丢弃 56 | return nil //丢弃这个TCPTransferMsg报文 57 | } 58 | default: //*protodf.TCPTransferMsg以外的报文直接返回 59 | return req 60 | } 61 | return nil 62 | } 63 | 64 | // 这个函数处理发往pool的消息,因为除了普通消息以外还有kick掉一个session这样的消息 65 | // 所以,这个函数里的要把kick消息区分出来,其他消息则转化成TCPTransferMsg 66 | func Router_CreateTCPTransferMsgByCommonMsg(req proto.Message, pBase *protodf.BaseInfo) proto.Message { 67 | switch data := req.(type) { 68 | case *protodf.TCPSessionKick: 69 | return data 70 | default: 71 | msg := new(protodf.TCPTransferMsg) 72 | protodf.SetBaseKindAndSubId(msg) 73 | protodf.CopyBaseExceptKindAndSubId(msg.Base, pBase) 74 | msg.DataKindId = pBase.KindId 75 | msg.DataSubId = pBase.SubId 76 | buf, err := proto.Marshal(req) 77 | if err != nil { 78 | return nil 79 | } else { 80 | msg.Data = buf 81 | return msg 82 | } 83 | } 84 | return req 85 | } 86 | -------------------------------------------------------------------------------- /LoginApp/LoginMainLogic.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/3zheng/railcommon" 7 | protodf "github.com/3zheng/railproto" 8 | proto "google.golang.org/protobuf/proto" 9 | ) 10 | 11 | type LoginMainLogic struct { 12 | mLogicPool *railcommon.SingleMsgPool //自身绑定的SingleMsgPool 13 | mDBPool *railcommon.SingleMsgPool //数据库的pool 14 | mRouterAgents *railcommon.RouterAgent //暂时一个router agent,以后可能会有多个 15 | mMyAppid uint32 16 | } 17 | 18 | // 实现railcommon.ILogicProcess的三个接口函数 19 | func (this *LoginMainLogic) Init(myPool *railcommon.SingleMsgPool) bool { 20 | this.mLogicPool = myPool 21 | return true 22 | } 23 | 24 | func (this *LoginMainLogic) ProcessReq(req proto.Message, pDatabase *railcommon.CADODatabase) { 25 | msg := Login_CreateCommonMsgByRouterTransferData(req) 26 | switch data := msg.(type) { 27 | case *PrivateInitMsg: 28 | this.Private_OnInit(data) 29 | case *protodf.LoginReq: 30 | this.Client_OnLoginReq(data) 31 | case *protodf.LoginRsp: 32 | this.Client_OnLoginRsp(data) 33 | default: 34 | return 35 | } 36 | } 37 | 38 | func (this *LoginMainLogic) OnPulse(ms uint64) { 39 | //定时调用程序 40 | } 41 | 42 | func (this *LoginMainLogic) Private_OnInit(req *PrivateInitMsg) { 43 | this.mMyAppid = req.myAppId 44 | this.mDBPool = req.pDBPool 45 | } 46 | 47 | func (this *LoginMainLogic) Client_OnLoginReq(req *protodf.LoginReq) { 48 | fmt.Println("收到了登录验证请求") 49 | //登录验证报文直接丢给DBPool 50 | this.PushToDBPool(req) 51 | } 52 | 53 | func (this *LoginMainLogic) Client_OnLoginRsp(req *protodf.LoginRsp) { 54 | fmt.Println("收到了登录验证回复") 55 | //直接把回复发往相应gate 56 | this.SendToOtherApp(req, req.Base) 57 | } 58 | 59 | // 向客户端发送 60 | func (this *LoginMainLogic) SendToUserClient(req proto.Message, pBase *protodf.BaseInfo, userId uint64, gateConnId uint64) { 61 | //向客户端发送消息,要先转为protodf.RouterTransferData,让router中转到gate 62 | msg := Login_CreateRouterTransferDataByCommonMsg(req, pBase) 63 | switch msg := msg.(type) { 64 | case *protodf.RouterTransferData: 65 | msg.DestAppid = pBase.AttAppid 66 | msg.DestApptype = uint32(protodf.EnumAppType_Gate) 67 | msg.DataDirection = protodf.RouterTransferData_App2Client //发往用户客户端 68 | msg.ClientRemoteAddress = pBase.RemoteAdd 69 | msg.AttGateid = pBase.AttAppid 70 | msg.AttUserid = userId 71 | msg.AttGateconnid = gateConnId 72 | } 73 | //这里只有SendMsgToServerAppByRouter,因为并没有绑定netagent 74 | this.mLogicPool.SendMsgToServerAppByRouter(msg) 75 | } 76 | 77 | // 向某个APP发送 78 | func (this *LoginMainLogic) SendToOtherApp(req proto.Message, pBase *protodf.BaseInfo) { 79 | //向客户端发送消息,要先转为protodf.RouterTransferData,让router中转到gate 80 | msg := Login_CreateRouterTransferDataByCommonMsg(req, pBase) 81 | switch msg := msg.(type) { 82 | case *protodf.RouterTransferData: 83 | msg.DestAppid = pBase.AttAppid 84 | msg.DestApptype = pBase.AttApptype 85 | msg.DataDirection = protodf.RouterTransferData_App2App //发往其他app 86 | fmt.Println("RouterTransferData的DestAppid=", msg.DestAppid, ",DestApptype=", msg.DestApptype, "msg.DataDirection=", msg.DataDirection) 87 | } 88 | //这里只有SendMsgToServerAppByRouter,因为并没有绑定netagent 89 | this.mLogicPool.SendMsgToServerAppByRouter(msg) 90 | } 91 | 92 | // 向DBPOOL发送(伪),这个发送实际上不走TCP/IP,是程序内部间的“发送” 93 | func (this *LoginMainLogic) PushToDBPool(req proto.Message) { 94 | if this.mDBPool != nil { 95 | this.mDBPool.PushMsg(req, 0) //往mDBPool的队列尾推入消息 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /ClientForTest/main.go: -------------------------------------------------------------------------------- 1 | // ClientForTest project main.go 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | 7 | "github.com/3zheng/railcommon" 8 | protodf "github.com/3zheng/railproto" 9 | proto "google.golang.org/protobuf/proto" 10 | ) 11 | 12 | func main() { 13 | //创建连接GATE的客户端 14 | ch := make(chan proto.Message, 1000) 15 | session := railcommon.CreateClient("127.0.0.1:4101", ch) 16 | if session == nil { 17 | fmt.Println("连接gate失败") 18 | return 19 | } 20 | go ReceiveMsg(ch) 21 | //发送登录请求 22 | loginReq := new(protodf.LoginReq) 23 | loginReq.LoginAccount = "yourname" 24 | loginReq.LoginPassword = "E10ADC3949BA59ABBE56E057F20F883E" //123456的MD5加密 25 | tcpMsg := ChangeCommonMsgToTCPTransferMsg(loginReq) 26 | if tcpMsg != nil { 27 | session.MsgWriteCh <- tcpMsg //向gate发送消息 28 | } 29 | fmt.Println("按任意键结束程序") 30 | sms := make([]byte, 50) 31 | _, err := fmt.Scan(&sms) 32 | if err != nil { 33 | fmt.Println("scan出错,err=", err) 34 | } 35 | } 36 | 37 | func ChangeCommonMsgToTCPTransferMsg(common proto.Message) *protodf.TCPTransferMsg { 38 | tcpMsg := new(protodf.TCPTransferMsg) 39 | gateMsg := new(protodf.GateTransferData) 40 | switch data := common.(type) { 41 | case *protodf.LoginReq: 42 | buff, err := proto.Marshal(data) 43 | if err != nil { 44 | return nil 45 | } 46 | gateMsg.Data = buff 47 | gateMsg.DataCmdKind = uint32(protodf.CMDKindId_IDKindClient) 48 | gateMsg.DataCmdSubid = uint32(protodf.CMDID_Client_IDLoginReq) 49 | gateMsg.AttApptype = uint32(protodf.EnumAppType_Login) //目标server app为登录app 50 | gateMsg.AttAppid = uint32(protodf.EnumAppId_Send2AnyOne) //随意一个登录app就行了,不需要知道其APPID 51 | default: 52 | fmt.Println("不识别的报文") 53 | return nil 54 | } 55 | tcpBuff, err := proto.Marshal(gateMsg) 56 | if err != nil { 57 | return nil 58 | } 59 | tcpMsg.Data = tcpBuff 60 | tcpMsg.DataKindId = uint32(protodf.CMDKindId_IDKindGate) 61 | tcpMsg.DataSubId = uint32(protodf.CMDID_Gate_IDTransferData) 62 | return tcpMsg 63 | } 64 | 65 | // 循环阻塞读取 66 | func ReceiveMsg(ch chan proto.Message) { 67 | for { 68 | select { 69 | case v, ok := <-ch: 70 | if !ok { 71 | return //跳出函数 72 | } 73 | switch data := v.(type) { 74 | case *protodf.TCPTransferMsg: 75 | fmt.Println("收到了gate的回复报文") 76 | //处理protodf.GateTransferData 77 | if data.DataKindId == uint32(protodf.CMDKindId_IDKindGate) && data.DataSubId == uint32(protodf.CMDID_Gate_IDTransferData) { 78 | fmt.Println("收到了protodf.GateTransferData") 79 | gateMsg := new(protodf.GateTransferData) 80 | err := proto.Unmarshal(data.Data, gateMsg) 81 | if err != nil { 82 | fmt.Println("proto反序列化失败") 83 | break 84 | } 85 | fmt.Println("attAppType=", gateMsg.AttApptype, "attAppID=", gateMsg.AttAppid) 86 | fmt.Println("gateMsg", gateMsg) 87 | if gateMsg.DataCmdKind == uint32(protodf.CMDKindId_IDKindClient) && gateMsg.DataCmdSubid == uint32(protodf.CMDID_Client_IDLoginRsp) { 88 | //登录回复 89 | fmt.Println("收到了protodf.LoginRsp报文") 90 | loginRsp := new(protodf.LoginRsp) 91 | err := proto.Unmarshal(gateMsg.Data, loginRsp) 92 | if err != nil { 93 | fmt.Println("proto反序列化失败") 94 | break 95 | } 96 | fmt.Println("loginRsp=", loginRsp) 97 | fmt.Println("收到登录回复,LoginResult=", loginRsp.LoginResult, ",UserId=", loginRsp.UserId) 98 | } 99 | } 100 | default: 101 | fmt.Println("收到了protodf.TCPTransferMsg以外的报文") 102 | } 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /GateApp/GateMsgFilter.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | protodf "github.com/3zheng/railproto" 7 | proto "google.golang.org/protobuf/proto" 8 | ) 9 | 10 | //gate消息过滤器,用于一般消息和protodf.TCPTransferMsg消息的相互转换 11 | 12 | // 这个函数处理来自pool的消息,因为pool里面的消息不止来源于net层,也可能来源于其他逻辑层或者自身的延时发送消息 13 | // 所以,这个函数里的消息不止有TCPTransferMsg,但是只需要处理TCPTransferMsg这个消息就行了 14 | // 把从TCPTransferMsg解析出来的gate应该处理的报文返回,不处理的报文直接丢弃返回nil 15 | func Gate_CreateCommonMsgByTCPTransferMsg(req proto.Message) proto.Message { 16 | switch data := req.(type) { 17 | case *protodf.TCPTransferMsg: 18 | fmt.Println("protodf.TCPTransferMsg base=", data.Base) 19 | switch data.DataKindId { //判断大类 20 | case uint32(protodf.CMDKindId_IDKindGate): 21 | switch data.DataSubId { //判断小类 22 | case uint32(protodf.CMDID_Gate_IDPulseReq): 23 | msg := new(protodf.PulseReq) 24 | err := proto.Unmarshal(data.Data, msg) 25 | protodf.SetBaseKindAndSubId(msg) 26 | protodf.CopyBaseExceptKindAndSubId(msg.Base, data.Base) 27 | if err != nil { 28 | fmt.Println("解析PulseReq出错") 29 | return nil 30 | } 31 | return msg 32 | case uint32(protodf.CMDID_Gate_IDPulseRsp): 33 | msg := new(protodf.PulseRsp) 34 | err := proto.Unmarshal(data.Data, msg) 35 | protodf.SetBaseKindAndSubId(msg) 36 | protodf.CopyBaseExceptKindAndSubId(msg.Base, data.Base) 37 | if err != nil { 38 | fmt.Println("解析PulseRsp出错") 39 | return nil 40 | } 41 | return msg 42 | case uint32(protodf.CMDID_Gate_IDTransferData): 43 | msg := new(protodf.GateTransferData) 44 | err := proto.Unmarshal(data.Data, msg) 45 | protodf.SetBaseKindAndSubId(msg) 46 | protodf.CopyBaseExceptKindAndSubId(msg.Base, data.Base) 47 | if err != nil { 48 | fmt.Println("解析TransferData出错") 49 | return nil 50 | } 51 | fmt.Println("成功解析TransferData") 52 | return msg 53 | default: 54 | fmt.Println("不识别的gate报文,DataSubId=", data.DataSubId) 55 | return nil //丢弃这个TCPTransferMsg报文 56 | } 57 | case uint32(protodf.CMDKindId_IDKindRouter): 58 | switch data.DataSubId { //判断小类 59 | case uint32(protodf.CMDID_Router_IDTransferDataRt): 60 | msg := new(protodf.RouterTransferData) 61 | err := proto.Unmarshal(data.Data, msg) 62 | protodf.SetBaseKindAndSubId(msg) 63 | protodf.CopyBaseExceptKindAndSubId(msg.Base, data.Base) 64 | if err != nil { 65 | fmt.Println("解析RouterTransferData出错") 66 | return nil 67 | } 68 | return msg 69 | default: 70 | fmt.Println("不识别的router报文,DataSubId=", data.DataSubId) 71 | return nil //丢弃这个TCPTransferMsg报文 72 | } 73 | default: //protodf.TCPTransferMsg报文的成员变量大类DataKindId不为CMDKindId_IDKindGate和CMDKindId_IDKindRouter都丢弃 74 | return nil //丢弃这个TCPTransferMsg报文 75 | } 76 | default: //*protodf.TCPTransferMsg以外的报文直接返回 77 | return req 78 | } 79 | return nil 80 | } 81 | 82 | // 这个函数处理发往pool的消息,因为除了普通消息以外还有kick掉一个session这样的消息 83 | // 所以,这个函数里的要把kick消息区分出来,其他消息则转化成TCPTransferMsg 84 | func Gate_CreateTCPTransferMsgByCommonMsg(req proto.Message, pBase *protodf.BaseInfo) proto.Message { 85 | switch data := req.(type) { 86 | case *protodf.TCPSessionKick: 87 | return data 88 | case *protodf.TCPTransferMsg: 89 | return data 90 | default: 91 | msg := new(protodf.TCPTransferMsg) 92 | protodf.SetBaseKindAndSubId(msg) 93 | protodf.CopyBaseExceptKindAndSubId(msg.Base, pBase) 94 | msg.DataKindId = pBase.KindId 95 | msg.DataSubId = pBase.SubId 96 | buf, err := proto.Marshal(req) 97 | if err != nil { 98 | return nil 99 | } else { 100 | msg.Data = buf 101 | return msg 102 | } 103 | } 104 | return req 105 | } 106 | 107 | func Gate_CreateCommonMsgByRouterTransferMsg(req *protodf.RouterTransferData) proto.Message { 108 | if req == nil { 109 | return nil 110 | } 111 | 112 | //大类 113 | switch req.DataCmdKind { 114 | case uint32(protodf.CMDKindId_IDKindClient): 115 | //小类 116 | switch req.DataCmdSubid { 117 | case uint32(protodf.CMDID_Client_IDLoginRsp): 118 | msg := new(protodf.LoginRsp) 119 | err := proto.Unmarshal(req.Data, msg) 120 | if err != nil { 121 | fmt.Println("LoginRsp解析失败") 122 | return nil 123 | } 124 | protodf.SetBaseKindAndSubId(msg) 125 | protodf.SetCommonMsgBaseByRouterTransferData(msg.Base, req) 126 | return msg 127 | default: 128 | return nil 129 | } 130 | default: 131 | return nil 132 | } 133 | 134 | return nil 135 | } 136 | 137 | func Gate_CreateGateTransferMsgByCommonMsg(req proto.Message) *protodf.GateTransferData { 138 | gateTrans := new(protodf.GateTransferData) 139 | protodf.SetBaseKindAndSubId(gateTrans) 140 | 141 | switch data := req.(type) { 142 | case *protodf.RouterTransferData: 143 | //RouterTransferData要特殊对待,不是调用proto.Marshal 144 | protodf.CopyBaseExceptKindAndSubId(gateTrans.Base, data.Base) 145 | gateTrans.Base.ConnId = data.AttGateconnid 146 | gateTrans.DataCmdKind = data.DataCmdKind 147 | gateTrans.DataCmdSubid = data.DataCmdSubid 148 | gateTrans.Data = make([]byte, len(data.Data)) 149 | copy(gateTrans.Data, data.Data) //使用copy,让req可以被GateConnection 150 | gateTrans.AttAppid = data.SrcAppid 151 | gateTrans.AttApptype = data.SrcApptype 152 | gateTrans.ReqId = 0 153 | case *protodf.LoginRsp: 154 | fmt.Println("序列化protodf.LoginRsp, LoginRsp=", data) 155 | protodf.CopyBaseExceptKindAndSubId(gateTrans.Base, data.Base) 156 | buff, err := proto.Marshal(data) 157 | if err != nil { 158 | fmt.Println("LoginRsp序列化失败") 159 | return nil 160 | } 161 | gateTrans.Data = buff 162 | gateTrans.DataCmdKind = uint32(protodf.CMDKindId_IDKindClient) 163 | gateTrans.DataCmdSubid = uint32(protodf.CMDID_Client_IDLoginRsp) 164 | gateTrans.AttAppid = data.Base.AttAppid 165 | gateTrans.AttApptype = data.Base.AttApptype 166 | gateTrans.ClientRemoteAddress = data.Base.RemoteAdd 167 | fmt.Println("LoginRsp => gateTrans=", gateTrans) 168 | default: 169 | return nil 170 | } 171 | return gateTrans 172 | } 173 | -------------------------------------------------------------------------------- /RouterApp/RouterLogic.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "math/rand" 7 | "time" 8 | 9 | "github.com/3zheng/railcommon" 10 | protodf "github.com/3zheng/railproto" 11 | 12 | proto "google.golang.org/protobuf/proto" 13 | ) 14 | 15 | type RouterConnection struct { 16 | connId uint64 17 | isConnectted bool 18 | clientAdress string 19 | appId uint32 //对应的APP ID 20 | appType uint32 //对应的app type 21 | lastResponseTime int64 //time.Now().Unix(),最近一次的响应时间,用于判断是否因为超时需要断开这个链接 22 | } 23 | 24 | type RouterLogic struct { 25 | mPool *railcommon.SingleMsgPool //自身绑定的SingleMsgPool 26 | mListenAgent *railcommon.NetAgent 27 | mMyAppid uint32 28 | //有分为app id和app type两个map是因为:有时候发送消息并不需要指定appid,只需要发往实现这个功能的apptype就行了 29 | //又因为注册app和断开app都是低频事件,而报文发送是高频事件,所以mMapAppType的value使用的是slice而不是map[uint32]uint32 30 | mMapConnection map[uint64]uint32 //以conn id为key, app id为value的map 31 | mMapAppId map[uint32]RouterConnection //以app id为key的map 32 | mMapAppType map[uint32]([]uint32) //以app type为key, 同一个app type下的所有app id组成的数组为value的map 33 | mRandNum *rand.Rand //随机数,在send anyone app的时候用到 34 | } 35 | 36 | func (this *RouterLogic) Init(myPool *railcommon.SingleMsgPool) bool { 37 | this.mPool = myPool 38 | //创建以UnixNano为随机种子的随机数变量 39 | s2 := rand.NewSource(time.Now().UnixNano()) 40 | this.mRandNum = rand.New(s2) 41 | this.mMapConnection = make(map[uint64]uint32) 42 | this.mMapAppId = make(map[uint32]RouterConnection) 43 | this.mMapAppType = make(map[uint32]([]uint32)) 44 | return true 45 | } 46 | 47 | func (this *RouterLogic) ProcessReq(req proto.Message, pDatabase *railcommon.CADODatabase) { 48 | msg := Router_CreateCommonMsgByTCPTransferMsg(req) 49 | switch data := msg.(type) { 50 | case *PrivateInitMsg: 51 | this.Private_OnInit(data) 52 | case *protodf.TCPSessionCome: 53 | this.Network_OnConnOK(data) 54 | case *protodf.TCPSessionClose: 55 | this.Network_OnConnClose(data) 56 | case *protodf.RouterTransferData: 57 | this.Router_OnRouteDataReq(data) 58 | case *protodf.RegisterAppReq: 59 | this.Router_OnRegReq(data) 60 | default: 61 | return 62 | } 63 | } 64 | 65 | func (this *RouterLogic) OnPulse(ms uint64) { 66 | 67 | } 68 | 69 | func (this *RouterLogic) Private_OnInit(req *PrivateInitMsg) { 70 | this.mMyAppid = req.myAppId 71 | } 72 | 73 | func (this *RouterLogic) Router_OnRegReq(req *protodf.RegisterAppReq) { 74 | appId := req.AppId 75 | appType := req.AppType 76 | connId := req.Base.ConnId 77 | if _, ok1 := this.mMapConnection[connId]; !ok1 { 78 | //如果ok1不存在返回 79 | return 80 | } 81 | _, ok2 := this.mMapAppId[appId] 82 | if ok2 { 83 | fmt.Println("相同类型相同app_id的app已经注册了,不允许重复注册。apptype=", appType, "typename=", protodf.GetAppTypeName(appType), 84 | ",appid=", appId, ",connid=", req.Base.ConnId) 85 | log.Println("相同类型相同app_id的app已经注册了,不允许重复注册。apptype=", appType, "typename=", protodf.GetAppTypeName(appType), 86 | ",appid=", appId, ",connid=", req.Base.ConnId) 87 | this.CloseSession(req.Base.ConnId) 88 | return 89 | } 90 | //发送回复 91 | rsp := new(protodf.RegisterAppRsp) 92 | protodf.SetBaseKindAndSubId(rsp) 93 | protodf.CopyBaseExceptKindAndSubId(rsp.Base, req.Base) 94 | rsp.RegResult = 1 95 | this.SendToOtherApp(rsp, rsp.Base) 96 | 97 | //往mMapConnection,mMapAppId,mMapAppType里新增数据 98 | this.mMapConnection[connId] = appId 99 | this.mMapAppId[appId] = RouterConnection{ 100 | connId: connId, 101 | isConnectted: true, 102 | clientAdress: req.Base.RemoteAdd, 103 | appId: appId, 104 | appType: appType, 105 | lastResponseTime: time.Now().Unix()} 106 | 107 | typeElem, ok3 := this.mMapAppType[appType] 108 | if !ok3 { 109 | sliceAppId := make([]uint32, 0, 10) 110 | this.mMapAppType[appType] = sliceAppId 111 | typeElem, ok3 = this.mMapAppType[appType] 112 | } 113 | var exist bool = false 114 | for _, v := range typeElem { 115 | //查找相应appid是否已经存在 116 | if v == appId { 117 | exist = true 118 | break 119 | } 120 | } 121 | if exist { 122 | //对应的appId存在 123 | fmt.Println(fmt.Println("奇怪的错误,相同类型相同app_id的app在mMapAppType中找到了,", 124 | "但是却通过了前面的appid map和connid map的检查。apptype=", appType, 125 | ",appid=", appId, ",connid=", req.Base.ConnId)) 126 | } else { 127 | //对应的appId不存在,新增这个appId 128 | typeElem = append(typeElem, appId) 129 | this.mMapAppType[appType] = typeElem 130 | fmt.Println("mMapAppType=", this.mMapAppType) 131 | } 132 | 133 | fmt.Println("一个App注册来了,type=", appType, 134 | ",typename=", protodf.GetAppTypeName(appType), 135 | ",id=", appId) 136 | 137 | } 138 | 139 | func (this *RouterLogic) Router_OnRouteDataReq(req *protodf.RouterTransferData) { 140 | //---------------------------------------------------------------------- 141 | // 转发报文 142 | // 目前看起来,指定app与指定appType中的任何一个使用的较多 143 | //---------------------------------------------------------------------- 144 | 145 | connId := req.Base.ConnId 146 | destAppId := req.DestAppid 147 | destAppType := req.DestApptype 148 | //判断connid是否存在, 149 | if appId, ok1 := this.mMapConnection[connId]; !ok1 { 150 | fmt.Println("当前连接还没有注册,不转发其报文:connid=", connId) 151 | return 152 | } else { 153 | elem, ok2 := this.mMapAppId[appId] 154 | if ok2 { 155 | //更新最新响应时间 156 | elem.lastResponseTime = time.Now().Unix() 157 | this.mMapAppId[appId] = elem 158 | //根据从mMapAppId给req的srcApptype和srcAppId赋值,这个值以router为准,不以传过来的报文为准 159 | req.SrcApptype = elem.appType 160 | req.SrcAppid = elem.appId 161 | } 162 | } 163 | 164 | var sendResult bool = false 165 | switch destAppId { 166 | case uint32(protodf.EnumAppId_Send2AnyOne): 167 | //发给某种apptype下的任意一个appid 168 | sendResult = this.DeliverToAnyOneByType(req, destAppType) 169 | case uint32(protodf.EnumAppId_Send2All): 170 | //发给某个apptype下的所有appid 171 | sendResult = this.DeliverToAllByType(req, destAppType) 172 | default: 173 | sendResult = this.DeliverToAllByPointID(req, destAppType, destAppId) 174 | } 175 | if !sendResult { 176 | fmt.Println("目标APP无法找到,可能尚未注册", 177 | ",dest apptype=", req.DestApptype, 178 | ",dest appname=", protodf.GetAppTypeName(req.DestApptype), 179 | ",dest appid=", req.DestAppid, 180 | ",src apptype=", req.SrcApptype, 181 | ",src appname=", protodf.GetAppTypeName(req.SrcApptype), 182 | ",src appid=", req.SrcAppid, 183 | ",src connid=", req.Base.ConnId, 184 | ",cmd_kindid=", req.DataCmdKind, 185 | ",cmd_subid=", req.DataCmdSubid, 186 | ",userid=", req.AttUserid, 187 | ",gate connid=", req.AttGateconnid) 188 | } else { 189 | fmt.Println("已向目标APP发送报文", 190 | ",dest apptype=", req.DestApptype, 191 | ",dest appname=", protodf.GetAppTypeName(req.DestApptype), 192 | ",dest appid=", req.DestAppid, 193 | ",src apptype=", req.SrcApptype, 194 | ",src appname=", protodf.GetAppTypeName(req.SrcApptype), 195 | ",src appid=", req.SrcAppid, 196 | ",src connid=", req.Base.ConnId, 197 | ",cmd_kindid=", req.DataCmdKind, 198 | ",cmd_subid=", req.DataCmdSubid, 199 | ",userid=", req.AttUserid, 200 | ",gate connid=", req.AttGateconnid) 201 | } 202 | } 203 | 204 | func (this *RouterLogic) Network_OnConnOK(req *protodf.TCPSessionCome) { 205 | if _, ok := this.mMapConnection[req.Base.ConnId]; ok { 206 | //FIXME 一般这不可能发生 207 | fmt.Println("发生了不可能事件,有重复的connId发生,connId = ", req.Base.ConnId) 208 | } else { 209 | fmt.Println("新建了一个客户端连接,connId = ", req.Base.ConnId) 210 | this.mMapConnection[req.Base.ConnId] = 0 //直到收到regreq后才知道app id,先设为0 211 | } 212 | } 213 | 214 | func (this *RouterLogic) Network_OnConnClose(req *protodf.TCPSessionClose) { 215 | connId := req.Base.ConnId 216 | fmt.Println("conn_id=", req.Base.ConnId, "断开连接") 217 | appId, ok := this.mMapConnection[connId] 218 | if !ok { 219 | return 220 | } 221 | // SendLogoutToOnline(connId) //这个函数暂时为空 222 | idElem, ok2 := this.mMapAppId[appId] 223 | if ok2 && idElem.connId == connId { 224 | //找到mMapAppType里对应的app id 225 | if typeElem, ok3 := this.mMapAppType[idElem.appType]; ok3 { 226 | for i, v := range typeElem { 227 | if v == appId { 228 | //将v这个元素从typeElem里删除 229 | typeElem = append(typeElem[:i], typeElem[i+1:]...) 230 | this.mMapAppType[idElem.appType] = typeElem 231 | } 232 | } 233 | } 234 | //在conn_id相等的情况下才能干掉mMapAppId里的对应appId,不然的话就删错了有木有 235 | delete(this.mMapAppId, appId) 236 | } 237 | delete(this.mMapConnection, connId) 238 | } 239 | 240 | // 向客户端发送报文 241 | func (this *RouterLogic) SendToOtherApp(req proto.Message, pBase *protodf.BaseInfo) { 242 | msg := Router_CreateTCPTransferMsgByCommonMsg(req, pBase) 243 | this.mPool.SendMsgToClientByNetAgent(msg) 244 | } 245 | 246 | // 主动断开一个session连接 247 | func (this *RouterLogic) CloseSession(connId uint64) { 248 | kick := new(protodf.TCPSessionKick) 249 | protodf.SetBaseKindAndSubId(kick) 250 | kick.Base.ConnId = connId 251 | //通知tcpnet层断开这个session 252 | this.mPool.SendMsgToClientByNetAgent(kick) 253 | } 254 | 255 | // 向某种apptype下的任意一个appid发送报文 256 | func (this *RouterLogic) DeliverToAnyOneByType(req *protodf.RouterTransferData, destAppType uint32) bool { 257 | protodf.OutputMyLog("发送往任意的", protodf.GetAppTypeName(destAppType)) 258 | if req.DataDirection == protodf.RouterTransferData_App2Client { 259 | //如果是服务端发往客户端的报文。不接受发往任意gate,必须指定gate appid,因为发往任意gate没有意义,根本找不到对应用户 260 | protodf.OutputMyLog("服务端发往客户端的报文必须指定gate appid") 261 | return false 262 | } 263 | typeElem, ok2 := this.mMapAppType[destAppType] 264 | if !ok2 { 265 | protodf.OutputMyLog("找不到相应的AppType=", protodf.GetAppTypeName(destAppType)) 266 | return false 267 | } 268 | //先找到对应的app type然后随机选择属于这个app type的一个app id 269 | if size := len(typeElem); size > 0 { 270 | randIndex := this.mRandNum.Intn(size) //返回的是随机数对size求余后的范围在[0,size)的新随机数 271 | randAppId := typeElem[randIndex] 272 | idElem, ok3 := this.mMapAppId[randAppId] 273 | if !ok3 { 274 | protodf.OutputMyLog("找不到相应的AppId=", randAppId) 275 | return false 276 | } 277 | //修改connId然后就可以转发了 278 | req.Base.ConnId = idElem.connId 279 | this.SendToOtherApp(req, req.Base) 280 | } else { 281 | return false 282 | } 283 | return true 284 | } 285 | 286 | // 向某种apptype下的所有appid发送报文 287 | func (this *RouterLogic) DeliverToAllByType(req *protodf.RouterTransferData, destAppType uint32) bool { 288 | if req.DataDirection == protodf.RouterTransferData_App2Client && destAppType == uint32(protodf.EnumAppType_Gate) { 289 | //如果是服务端发往客户端的报文。接受发往所有gate,因为有可能是广播类型消息,但是要慎用,所以打印出来,以免滥用 290 | fmt.Println("此报文向将所有gate广播", 291 | ",dest apptype=", req.DestApptype, 292 | ",dest appname=", protodf.GetAppTypeName(req.DestApptype), 293 | ",dest appid=", req.DestAppid, 294 | ",src apptype=", req.SrcApptype, 295 | ",src appname=", protodf.GetAppTypeName(req.SrcApptype), 296 | ",src appid=", req.SrcAppid, 297 | ",src connid=", req.Base.ConnId, 298 | ",cmd_kindid=", req.DataCmdKind, 299 | ",cmd_subid=", req.DataCmdSubid, 300 | ",userid=", req.AttUserid, 301 | ",gate connid=", req.AttGateconnid) 302 | } 303 | 304 | typeElem, ok2 := this.mMapAppType[destAppType] 305 | if !ok2 { 306 | return false 307 | } 308 | //遍历typeElem并向每一个appid发送 309 | for _, appId := range typeElem { 310 | idElem, ok3 := this.mMapAppId[appId] 311 | if !ok3 { 312 | return false 313 | } 314 | //修改connId然后就可以转发了 315 | req.Base.ConnId = idElem.connId 316 | this.SendToOtherApp(req, req.Base) 317 | } 318 | return true 319 | } 320 | 321 | // 向指定appId发送报文 322 | func (this *RouterLogic) DeliverToAllByPointID(req *protodf.RouterTransferData, destAppType uint32, destAppId uint32) bool { 323 | if req.DataDirection == protodf.RouterTransferData_App2Client && destAppType != uint32(protodf.EnumAppType_Gate) { 324 | //如果是服务端发往客户端的报文。却不是发往gate,那就是填错了 325 | return false 326 | } 327 | 328 | idElem, ok3 := this.mMapAppId[destAppId] 329 | if !ok3 { 330 | return false 331 | } 332 | //验证报文的destAppType是否正确 333 | if idElem.appType != destAppType { 334 | return false 335 | } 336 | //修改connId然后就可以转发了 337 | req.Base.ConnId = idElem.connId 338 | this.SendToOtherApp(req, req.Base) 339 | return true 340 | } 341 | -------------------------------------------------------------------------------- /GateApp/GateLogic.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "reflect" 7 | "time" 8 | 9 | "github.com/3zheng/railcommon" 10 | protodf "github.com/3zheng/railproto" 11 | proto "google.golang.org/protobuf/proto" 12 | ) 13 | 14 | type GateConnection struct { 15 | connId uint64 16 | isConnectted bool 17 | clientAdress string //IP地址含端口,格式192.168.1.1:10010 18 | userId uint64 //对应的用户ID 19 | isAuthenticated bool //在登录成功后isAuthenticated为true 20 | connectedTime int64 //time.Now().Unix(),建立起连接的时间 21 | lastResponseTime int64 //time.Now().Unix(),最近一次的响应时间,用于判断是否因为超时需要断开这个链接 22 | msgFromClientNum uint64 //来自这个连接的报文数,有恶意发包行为可以踢掉之类的 23 | } 24 | 25 | type GateUserInfo struct { 26 | userId uint64 27 | connId uint64 28 | } 29 | 30 | // *GateLogic继承于LogicProcess接口 31 | type GateLogic struct { 32 | mPool *railcommon.SingleMsgPool //自身绑定的SingleMsgPool 33 | mListenAgent *railcommon.NetAgent 34 | mRouterAgents *railcommon.RouterAgent //暂时一个router agent,以后可能会有多个 35 | mMyAppid uint32 36 | mMapConnection map[uint64]GateConnection //以connId为key的map 37 | mMapUser map[uint64]GateUserInfo //以userId为key的map 38 | } 39 | 40 | // 实现railcommon.ILogicProcess的三个接口函数 41 | func (this *GateLogic) Init(myPool *railcommon.SingleMsgPool) bool { 42 | this.mPool = myPool 43 | this.mMapConnection = make(map[uint64]GateConnection) 44 | this.mMapUser = make(map[uint64]GateUserInfo) 45 | return true 46 | } 47 | 48 | func (this *GateLogic) ProcessReq(req proto.Message, pDatabase *railcommon.CADODatabase) { 49 | if req == nil { 50 | return 51 | } 52 | msg := Gate_CreateCommonMsgByTCPTransferMsg(req) 53 | switch data := msg.(type) { 54 | case *PrivateInitMsg: 55 | this.Private_OnInit(data) 56 | case *protodf.TCPSessionCome: 57 | this.Network_OnConnOK(data) 58 | case *protodf.TCPSessionClose: 59 | this.Network_OnConnClose(data) 60 | case *protodf.GateTransferData: 61 | this.Gate_GateTransferData(data) 62 | case *protodf.PulseReq: 63 | this.Gate_PulseReq(data) 64 | case *protodf.RouterTransferData: 65 | this.Router_OnRouterTransferData(data) 66 | case *protodf.LoginRsp: 67 | this.Client_OnLoginRsp(data) 68 | default: 69 | if data != nil { 70 | fmt.Println("不识别的报文,string=", reflect.TypeOf(data).String()) 71 | } 72 | } 73 | } 74 | 75 | func (this *GateLogic) OnPulse(ms uint64) { 76 | //定时调用程序 77 | } 78 | 79 | func (this *GateLogic) Private_OnInit(req *PrivateInitMsg) { 80 | this.mMyAppid = req.myAppId 81 | } 82 | 83 | // 新建了一个客户端session 84 | func (this *GateLogic) Network_OnConnOK(req *protodf.TCPSessionCome) { 85 | fmt.Println("收到了TCPSessionCome报文") 86 | if req.Base.ConnId > 0xefffffff { 87 | fmt.Println("报告大王,连接快被分配完了,快点采取行动") 88 | log.Println("报告大王,连接快被分配完了,快点采取行动") 89 | } 90 | 91 | if _, ok := this.mMapConnection[req.Base.ConnId]; ok { 92 | //FIXME 一般这不可能发生 93 | fmt.Println("发生了不可能事件,有重复的connId发生,connId = ", req.Base.ConnId) 94 | log.Println("发生了不可能事件,有重复的connId发生,connId = ", req.Base.ConnId) 95 | } else { 96 | fmt.Println("新建了一个客户端连接,connId = ", req.Base.ConnId) 97 | log.Println("新建了一个客户端连接,connId = ", req.Base.ConnId) 98 | unixNow := time.Now().Unix() 99 | this.mMapConnection[req.Base.ConnId] = GateConnection{ 100 | connId: req.Base.ConnId, 101 | clientAdress: req.Base.RemoteAdd, 102 | userId: 0, 103 | isAuthenticated: false, 104 | connectedTime: unixNow, 105 | lastResponseTime: unixNow} 106 | } 107 | 108 | } 109 | 110 | // 断开了一个客户端session 111 | func (this *GateLogic) Network_OnConnClose(req *protodf.TCPSessionClose) { 112 | connId := req.Base.ConnId 113 | fmt.Println("conn_id=", req.Base.ConnId, "断开连接") 114 | log.Println("conn_id=", req.Base.ConnId, "断开连接") 115 | connElem, ok := this.mMapConnection[connId] 116 | if !ok { 117 | return 118 | } 119 | // SendLogoutToOnline(connId) //这个函数暂时为空 120 | connElem.isConnectted = false 121 | userElem, ok2 := this.mMapUser[connElem.userId] 122 | if ok2 && userElem.connId == connId { 123 | //在conn_id相等的情况下才能干掉m_map_user里的对应user_id,不然的话就删错了有木有 124 | delete(this.mMapUser, connElem.userId) 125 | } 126 | delete(this.mMapConnection, connId) 127 | } 128 | 129 | // 收到了客户端的心跳测试请求 130 | func (this *GateLogic) Gate_PulseReq(req *protodf.PulseReq) { 131 | //发送回复 132 | rsp := new(protodf.PulseRsp) 133 | protodf.SetBaseKindAndSubId(rsp) 134 | rsp.Base.ConnId = req.Base.ConnId 135 | rsp.SpeedData = uint32(time.Now().Unix()) 136 | this.SendToClient(rsp, rsp.Base) 137 | } 138 | 139 | // 收到了客户端传来的消息 140 | func (this *GateLogic) Gate_GateTransferData(req *protodf.GateTransferData) { 141 | connElem, ok := this.mMapConnection[req.Base.ConnId] 142 | if !ok { 143 | //FIXME logger 144 | fmt.Println("找不到对应connId=", req.Base.ConnId) 145 | log.Println("找不到对应connId=", req.Base.ConnId) 146 | return 147 | } 148 | 149 | connElem.msgFromClientNum++ 150 | connElem.lastResponseTime = time.Now().Unix() 151 | 152 | routerMsg := new(protodf.RouterTransferData) 153 | protodf.SetBaseKindAndSubId(routerMsg) 154 | protodf.CopyBaseExceptKindAndSubId(routerMsg.Base, req.Base) 155 | 156 | routerMsg.DestAppid = req.AttAppid 157 | routerMsg.DestApptype = req.AttApptype 158 | routerMsg.DataCmdKind = req.DataCmdKind 159 | routerMsg.DataCmdSubid = req.DataCmdSubid 160 | routerMsg.Data = make([]byte, len(req.Data)) 161 | copy(routerMsg.Data, req.Data) //这里使用copy重新分配一块内存块,然后req就可以被GC了,可能routerMsg.Data = req.Data更好,可以跑跑试试 162 | //FIXME 这里需要取得自己的app_type与id 163 | routerMsg.SrcAppid = this.mMyAppid 164 | routerMsg.SrcApptype = uint32(protodf.EnumAppType_Gate) 165 | routerMsg.DataDirection = protodf.RouterTransferData_Client2App 166 | routerMsg.AttGateid = this.mMyAppid 167 | routerMsg.AttUserid = connElem.userId 168 | routerMsg.AttGateconnid = connElem.connId 169 | routerMsg.ClientRemoteAddress = connElem.clientAdress 170 | 171 | if routerMsg.DestAppid == 0 { 172 | fmt.Println("目标地址为0,来自client,connid=", connElem.connId, 173 | ",kind_id=", req.DataCmdKind, 174 | ",sub_id=", req.DataCmdSubid, 175 | ",my_user_id=", connElem.userId, 176 | ",dest_appid=", req.AttAppid, 177 | ",dest_apptype=", protodf.GetAppTypeName(req.AttApptype)) 178 | log.Println("目标地址为0,来自client,connid=", connElem.connId, 179 | ",kind_id=", req.DataCmdKind, 180 | ",sub_id=", req.DataCmdSubid, 181 | ",my_user_id=", connElem.userId, 182 | ",dest_appid=", req.AttAppid, 183 | ",dest_apptype=", protodf.GetAppTypeName(req.AttApptype)) 184 | return //且不往router发送了 185 | } 186 | //向router发送消息 187 | fmt.Println("向router发送客户端消息") 188 | this.mPool.SendMsgToServerAppByRouter(routerMsg) 189 | } 190 | 191 | func (this *GateLogic) Router_OnRouterTransferData(req *protodf.RouterTransferData) { 192 | 193 | //转发报文,应当只会是那类需要转到客户端的 194 | //不是转发到客户端的报文,在解析成普通报文后重新推送进自己的mPool中,是否要处理,在processReq入口处决定 195 | // 196 | 197 | //这里发送对象以userId为准,如果userId为0才去使用gateconnid来查找相应发送对象 198 | gateConnId := req.AttGateconnid 199 | if req.DataDirection == protodf.RouterTransferData_App2Client { 200 | userId := req.AttUserid 201 | if userId == 0 && gateConnId == 0 { 202 | fmt.Println("报告大王,有个搞笑的家伙发来了user_id和conn_id均为0的报文。叫我女王大人。好的大王,没问题大王!") 203 | fmt.Println("这个家伙是, 来自router,connid=", req.AttGateconnid, 204 | ",kind_id=", req.DataCmdKind, 205 | ",sub_id=", req.DataCmdSubid, 206 | ",att_user_id=", req.AttUserid, 207 | ",src_appid=", req.SrcAppid, 208 | ",src_apptype=", req.SrcApptype) 209 | //可能需要通知写这个APP的程序猿 210 | log.Println("user_id和conn_id均为0的报文, 来自router,connid=", req.AttGateconnid, 211 | ",kind_id=", req.DataCmdKind, 212 | ",sub_id=", req.DataCmdSubid, 213 | ",att_user_id=", req.AttUserid, 214 | ",src_appid=", req.SrcAppid, 215 | ",src_apptype=", req.SrcApptype) 216 | return 217 | } 218 | 219 | if userId != 0 { 220 | //如果user_id不为0,则发送对象以user_id为准,因为gate_connid在顶号登录时,其他APP没有同步更新得到新的conn_id,这样就会把报文发给已经断线的客户端 221 | if userElem, ok := this.mMapUser[userId]; !ok { 222 | //如果用户不为0,又在mMapUser找不到对应用户,说明用户已断线离开,直接return 223 | fmt.Println("用户已找不到,无法将该报文发往指定用户,可能该用户已经掉线,user_id=", userId) 224 | log.Println("用户已找不到,无法将该报文发往指定用户,可能该用户已经掉线,user_id=", userId) 225 | return 226 | } else { 227 | //对gateConnId重新赋值,当userId不为0时,以userId查map所得的connId为准 228 | gateConnId = userElem.connId 229 | } 230 | } 231 | 232 | connElem, ok := this.mMapConnection[gateConnId] 233 | if !ok { 234 | fmt.Println("找不到相应connId, 来自router,connid=", req.AttGateconnid, 235 | ",kind_id=", req.DataCmdKind, 236 | ",sub_id=", req.DataCmdSubid, 237 | ",my_user_id=", connElem.userId, 238 | ",att_user_id=", req.AttUserid, 239 | ",src_appid=", req.SrcAppid, 240 | ",src_apptype=", req.SrcApptype) 241 | log.Println("找不到相应connId, 来自router,connid=", req.AttGateconnid, 242 | ",kind_id=", req.DataCmdKind, 243 | ",sub_id=", req.DataCmdSubid, 244 | ",my_user_id=", connElem.userId, 245 | ",att_user_id=", req.AttUserid, 246 | ",src_appid=", req.SrcAppid, 247 | ",src_apptype=", req.SrcApptype) 248 | } 249 | 250 | if req.AttUserid != 0 && connElem.isAuthenticated != true { 251 | //若已经成功登录以后的 252 | fmt.Println("该连接的用户尚未认证,但是却收到了发往该用户的报文,user_id=", req.AttUserid) 253 | log.Println("该连接的用户尚未认证,但是却收到了发往该用户的报文,user_id=", req.AttUserid) 254 | return 255 | } else { 256 | if gateConnId != req.AttGateconnid { 257 | //就是在if userElem, ok := this.mMapUser[userId]; !ok被重新赋值了 258 | fmt.Println("报文的gateconnid与gate自己记录的connid不匹配,可能该账号是顶号登录的", 259 | ",报文connid=", req.AttGateconnid, 260 | ",实际connid=", gateConnId, 261 | ",user_id=", userId, 262 | ",src_appid=", req.SrcAppid) 263 | log.Println("报文的gateconnid与gate自己记录的connid不匹配,可能该账号是顶号登录的", 264 | ",报文connid=", req.AttGateconnid, 265 | ",实际connid=", gateConnId, 266 | ",user_id=", userId, 267 | ",src_appid=", req.SrcAppid) 268 | } 269 | req.AttGateconnid = gateConnId 270 | this.SendToClient(req, req.Base) 271 | } 272 | 273 | } else if req.DataDirection == protodf.RouterTransferData_App2App { 274 | //这个是发给gate自己的,而不是发给客户端的,需要gate app来处理 275 | fmt.Println("收到了需要gate自己来处理的报文,SrcApptype=", req.SrcApptype, "SrcAppid=", req.SrcAppid, 276 | "DataCmdKind=", req.DataCmdKind, "DataCmdSubid=", req.DataCmdSubid) 277 | log.Println("收到了需要gate自己来处理的报文,SrcApptype=", req.SrcApptype, "SrcAppid=", req.SrcAppid, 278 | "DataCmdKind=", req.DataCmdKind, "DataCmdSubid=", req.DataCmdSubid) 279 | msg := Gate_CreateCommonMsgByRouterTransferMsg(req) 280 | if msg != nil { 281 | this.mPool.PushMsg(msg, 0) //推送到pool的chan队列尾 282 | } 283 | } else { //clientToApp,这是不可能的 284 | //from client? 285 | //FIXME log 286 | //you should kick this connection 287 | } 288 | 289 | } 290 | 291 | func (this *GateLogic) AppFrame_OnClientAuth(req proto.Message) { 292 | 293 | } 294 | 295 | // 登录回复报文 296 | func (this *GateLogic) Client_OnLoginRsp(req *protodf.LoginRsp) { 297 | fmt.Println("收到了LoginRsp, string=", req.String(), "gate connId=", req.Base.GateConnId) 298 | //收到了登录回复报文表示验证成功了 299 | connId := req.Base.GateConnId 300 | 301 | userId := req.UserId 302 | connElem, ok := this.mMapConnection[connId] 303 | if !ok { 304 | protodf.OutputMyLog("不存在的connId=", connId) 305 | return 306 | } 307 | connElem.isAuthenticated = true 308 | connElem.userId = userId 309 | this.mMapConnection[connId] = connElem 310 | //登录成功的情况下才记录userId 311 | if userId != 0 && req.LoginResult == protodf.LoginRsp_SUCCESS { 312 | //查mMapUser 313 | userElem, ok := this.mMapUser[userId] 314 | if ok { 315 | //如果存在,要做顶号处理,把之前的连接断开,不能一个userId维持两个连接 316 | kick := new(protodf.TCPSessionKick) //userElem.connId就是要断开的connId 317 | kick.Base.ConnId = userElem.connId 318 | this.SendToClient(kick, kick.Base) 319 | } 320 | userElem.userId = userId 321 | userElem.connId = connId 322 | this.mMapUser[userId] = userElem 323 | } 324 | req.Base.ConnId = connId 325 | fmt.Println("向client发送,将Base.ConnId重置后发送") 326 | log.Println("向client发送,将Base.ConnId重置后发送") 327 | this.SendToClient(req, req.Base) 328 | } 329 | 330 | // 向客户端发送报文 331 | func (this *GateLogic) SendToClient(req proto.Message, pBase *protodf.BaseInfo) { 332 | //先把其他报文转成protodf.TransferData然后再转成protodf.TCPTransferMsg 333 | var gateTrans *protodf.GateTransferData = nil 334 | gateTrans = Gate_CreateGateTransferMsgByCommonMsg(req) 335 | if gateTrans != nil { 336 | //将protodf.TransferData报文转为protodf.TCPTransferMsg 337 | msg := Gate_CreateTCPTransferMsgByCommonMsg(gateTrans, gateTrans.Base) 338 | this.mPool.SendMsgToClientByNetAgent(msg) 339 | } else { 340 | //非protodf.TransferData报文就直接把req转为protodf.TCPTransferMsg 341 | msg := Gate_CreateTCPTransferMsgByCommonMsg(req, pBase) 342 | this.mPool.SendMsgToClientByNetAgent(msg) 343 | } 344 | 345 | } 346 | 347 | // 主动断开一个session连接 348 | func (this *GateLogic) CloseSession(connId uint64) { 349 | kick := new(protodf.TCPSessionKick) 350 | protodf.SetBaseKindAndSubId(kick) 351 | kick.Base.ConnId = connId 352 | //通知tcpnet层断开这个session 353 | this.mPool.SendMsgToClientByNetAgent(kick) 354 | } 355 | --------------------------------------------------------------------------------