├── .vscode └── settings.json ├── bin └── 此文件夹必须 ├── pkg └── windows_amd64 │ ├── cg.a │ └── ipc.a └── src ├── cg ├── center.go ├── centerclient.go └── player.go ├── cgss.go └── ipc ├── client.go ├── ipc_test.go └── server.go /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // 将设置放入此文件中以覆盖默认值和用户设置。 2 | { 3 | "go.gopath": "D:\\go_workspace\\cgss" 4 | } -------------------------------------------------------------------------------- /bin/此文件夹必须: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuqingliu/cgss/ab9c849871f4d85635ed7e595e7d777898f62dd0/bin/此文件夹必须 -------------------------------------------------------------------------------- /pkg/windows_amd64/cg.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuqingliu/cgss/ab9c849871f4d85635ed7e595e7d777898f62dd0/pkg/windows_amd64/cg.a -------------------------------------------------------------------------------- /pkg/windows_amd64/ipc.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuqingliu/cgss/ab9c849871f4d85635ed7e595e7d777898f62dd0/pkg/windows_amd64/ipc.a -------------------------------------------------------------------------------- /src/cg/center.go: -------------------------------------------------------------------------------- 1 | //添加用户、删除用户、列出用户和广播 2 | package cg 3 | 4 | import ( 5 | "encoding/json" 6 | "errors" 7 | "ipc" 8 | "sync" //引入”锁“的包 9 | ) 10 | 11 | var _ ipc.Server = &CenterServer{} //确认实现了server接口 12 | 13 | type Message struct { //message消息内容结构体 14 | From string "from" 15 | To string "to" 16 | Content string "content" 17 | } 18 | 19 | type Room struct { //房间结构体 20 | } 21 | 22 | type CenterServer struct { //中央处理服务结构体内容 23 | servers map[string]ipc.Server //类型为map[string]ipc.Server的map参数 24 | players []*Player //类型为player的参数 25 | rooms []*Room //类型为room的参数 26 | mutex sync.RWMutex //类型为sync.RWMutex的读写锁 27 | } 28 | 29 | func NewCenterServer() *CenterServer { //创建返回值为centerserver类型指针的函数 30 | servers := make(map[string]ipc.Server) //创建实体servers,map类型 31 | players := make([]*Player, 0) //创建player类型的结构体 32 | return &CenterServer{servers: servers, players: players} //返回中央处理服务 33 | } 34 | 35 | func (server *CenterServer) addPlayer(params string) error { //添加player到中心服务器server的函数,传入参数params,返回值error 36 | player := NewPlayer() //创建一个player 37 | 38 | err := json.Unmarshal([]byte(params), &player) //反序列化json格式的player 39 | if err != nil { 40 | return err 41 | } 42 | 43 | server.mutex.Lock() //锁 44 | //解锁 ?为什么锁了后直接又解锁。。。是可以同时到锁这里,只有当解锁后才会添加下面的player! 45 | defer server.mutex.Unlock() 46 | //直接添加到登录到服务器上的players 47 | server.players = append(server.players, player) 48 | 49 | return nil 50 | } 51 | 52 | //在中心服务器server中删除player(下线) 53 | func (server *CenterServer) removePlayer(params string) error { 54 | //锁住,只能单请求 55 | server.mutex.Lock() 56 | defer server.mutex.Unlock() 57 | //循环当前服务器上在线的用户 58 | for i, v := range server.players { 59 | //判断在线用户中和要删除的用户名是否相同 60 | if v.Name == params { 61 | if len(server.players) == 1 { 62 | //若当前在线服务器上用户只有一个,则直接清空 63 | server.players = make([]*Player, 0) 64 | } else if i == len(server.players)-1 { 65 | //若当前找到的是最后一个位置的player,则取所有前面的players 66 | server.players = server.players[:i-1] 67 | } else if i == 0 { 68 | //若当前找到的是第一个位置的player,则取所有后面的players 69 | server.players = server.players[1:] 70 | } else { 71 | //若当前找到的是中间位置的player,则取所有前面,和所有后面,除去当前i位置 72 | server.players = append(server.players[:i-1], server.players[:i+1]...) 73 | } 74 | return nil 75 | } 76 | } 77 | 78 | return errors.New("Player not found") 79 | } 80 | 81 | //在线服务器展示所有player玩家信息 82 | func (server *CenterServer) listPlayer(params string) (players string, err error) { 83 | server.mutex.RLock() 84 | defer server.mutex.RUnlock() 85 | 86 | if len(server.players) > 0 { 87 | //json化players 88 | b, _ := json.Marshal(server.players) 89 | players = string(b) 90 | } else { 91 | err = errors.New("No play online.") 92 | } 93 | 94 | return 95 | } 96 | 97 | //在线服务器广播消息 98 | func (server *CenterServer) broadCast(params string) error { 99 | var message Message 100 | //将传入的消息反序列化 101 | err := json.Unmarshal([]byte(params), &message) 102 | if err != nil { 103 | return err 104 | } 105 | 106 | server.mutex.Lock() 107 | defer server.mutex.Unlock() 108 | 109 | if len(server.players) > 0 { 110 | for _, player := range server.players { 111 | //将广播的消息传入给每个player 112 | player.mq <- &message 113 | } 114 | } else { 115 | err = errors.New("No player online.") 116 | } 117 | 118 | return err 119 | } 120 | 121 | //在线服务器指令处理方法 122 | func (server *CenterServer) Handle(method, params string) *ipc.Response { 123 | switch method { 124 | //添加在线玩家 125 | case "addplayer": 126 | err := server.addPlayer(params) 127 | if err != nil { 128 | return &ipc.Response{Code: err.Error()} 129 | } 130 | //移除在线玩家 131 | case "removeplayer": 132 | err := server.removePlayer(params) 133 | if err != nil { 134 | return &ipc.Response{Code: err.Error()} 135 | } 136 | //显示所有玩家 137 | case "listplayer": 138 | players, err := server.listPlayer(params) 139 | if err != nil { 140 | return &ipc.Response{Code: err.Error()} 141 | } 142 | return &ipc.Response{"200", players} 143 | //广播消息 144 | case "broadcast": 145 | err := server.broadCast(params) 146 | if err != nil { 147 | return &ipc.Response{Code: err.Error()} 148 | } 149 | return &ipc.Response{Code: "200"} 150 | //错误指令处理 151 | default: 152 | return &ipc.Response{Code: "404", Body: method + ":" + params} 153 | } 154 | return &ipc.Response{Code: "200"} 155 | } 156 | 157 | //返回所需要的中心服务器名称 158 | func (server *CenterServer) Name() string { 159 | return "CenterServer" 160 | } 161 | -------------------------------------------------------------------------------- /src/cg/centerclient.go: -------------------------------------------------------------------------------- 1 | //CenterClient匿名组合了IpcClient 2 | package cg 3 | 4 | import ( 5 | "encoding/json" 6 | "errors" 7 | 8 | "ipc" 9 | ) 10 | 11 | type CenterClient struct { //中央服务器客户端 12 | *ipc.IpcClient 13 | } 14 | 15 | //中央服务器添加新用户 16 | func (client *CenterClient) AddPlayer(player *Player) error { 17 | b, err := json.Marshal(*player) 18 | if err != nil { 19 | return err 20 | } 21 | //调用ipc客户端的call方法 22 | resp, err := client.Call("addplayer", string(b)) 23 | if err == nil && resp.Code == "200" { 24 | return nil 25 | } 26 | 27 | return err 28 | } 29 | 30 | //中央服务器删除用户 31 | func (client *CenterClient) RemovePlayer(name string) error { 32 | ret, _ := client.Call("removeplayer", name) 33 | if ret.Code == "200" { 34 | return nil 35 | } 36 | 37 | return errors.New(ret.Code) 38 | } 39 | 40 | //中央服务器展示用户列表 41 | func (client *CenterClient) ListPlayer(params string) (ps []*Player, err error) { 42 | resp, _ := client.Call("listplayer", params) 43 | if resp.Code != "200" { 44 | err = errors.New(resp.Code) 45 | return 46 | } 47 | 48 | err = json.Unmarshal([]byte(resp.Body), &ps) 49 | return 50 | } 51 | 52 | //中央服务器广播消息 53 | func (client *CenterClient) Broadcast(message string) error { 54 | m := &Message{Content: message} 55 | 56 | b, err := json.Marshal(m) 57 | if err != nil { 58 | return err 59 | } 60 | resp, _ := client.Call("broadcast", string(b)) 61 | if resp.Code == "200" { 62 | return nil 63 | } 64 | 65 | return errors.New(resp.Code) 66 | } 67 | -------------------------------------------------------------------------------- /src/cg/player.go: -------------------------------------------------------------------------------- 1 | //在线玩家的管理 2 | //我们为每个玩家都起了一个独立的goroutine,监听所有发送给他们 3 | //的聊天信息,一旦收到就即时打印到控制台上 4 | package cg 5 | 6 | import ( 7 | "fmt" 8 | ) 9 | 10 | type Player struct { //定义player结构体属性内容 11 | Name string "name" //姓名 12 | Level int "level" //等级 13 | Exp int "exp" // 14 | Room int "room" //房间号 15 | mq chan *Message //等待收取的消息 16 | } 17 | 18 | func NewPlayer() *Player { //函数NewPlayer创建player 19 | m := make(chan *Message, 1024) //创建一个缓冲区为1024的channel,类型为message 20 | player := &Player{"", 0, 0, 0, m} //创建一个player,赋初值 21 | 22 | go func(p *Player) { // gorountine 匿名函数,处理实体player,参数p为player类 23 | for { 24 | msg := <-p.mq //获取player类channel里的p实体mq值 25 | fmt.Println(p.Name, "received message:", msg.Content) //打印收到message 26 | } 27 | }(player) 28 | 29 | return player 30 | } 31 | -------------------------------------------------------------------------------- /src/cgss.go: -------------------------------------------------------------------------------- 1 | //主程序具备模拟用户游戏过程和管理员功能(发通告) 2 | package main 3 | 4 | import ( 5 | "bufio" 6 | "fmt" 7 | "os" 8 | "strconv" 9 | "strings" 10 | 11 | "cg" 12 | "ipc" 13 | ) 14 | 15 | var centerClient *cg.CenterClient //定义中央服务器 16 | 17 | //启动中央服务器,通过ipc去创建服务器和客户端 18 | func startCenterService() error { 19 | server := ipc.NewIpcServer(&cg.CenterServer{}) //服务器 20 | client := ipc.NewIpcClient(server) //客户端 21 | centerClient = &cg.CenterClient{client} //服务器指令 22 | 23 | return nil 24 | } 25 | 26 | //帮助指令 27 | func Help(args []string) int { 28 | fmt.Println(` 29 | Commands: 30 | login 31 | logout 32 | send 33 | listplayer 34 | quit(q) 35 | help(h) 36 | `) 37 | return 0 38 | } 39 | 40 | //退出程序 41 | func Quit(args []string) int { 42 | return 1 43 | } 44 | 45 | //退出“中心服务器” 46 | func Logout(args []string) int { 47 | //判断是否为2个参数 48 | if len(args) != 2 { 49 | fmt.Println("USAGE: logout ") 50 | return 0 51 | } 52 | //中心服务器处理退出玩家 53 | centerClient.RemovePlayer(args[1]) 54 | 55 | return 0 56 | } 57 | 58 | //登录”中心服务器“ 59 | func Login(args []string) int { 60 | //4个参数 61 | if len(args) != 4 { 62 | fmt.Println("USAGE: login ") 63 | return 0 64 | } 65 | //第3个参数验证 66 | level, err := strconv.Atoi(args[2]) 67 | if err != nil { 68 | fmt.Println("Invaild Parameter : should be an integer.") 69 | return 0 70 | } 71 | //第4个参数验证 72 | exp, err := strconv.Atoi(args[3]) 73 | if err != nil { 74 | fmt.Println("Invaild Parameter : should be an integer.") 75 | return 0 76 | } 77 | //创建新玩家,并将传入的参数赋值 78 | player := cg.NewPlayer() 79 | player.Name = args[1] 80 | player.Level = level 81 | player.Exp = exp 82 | //中心服务器添加玩家 83 | err = centerClient.AddPlayer(player) 84 | if err != nil { 85 | fmt.Println("Faild adding player", err) 86 | } 87 | 88 | return 0 89 | } 90 | 91 | //展示所有玩家 92 | func ListPlayer(args []string) int { 93 | ps, err := centerClient.ListPlayer("") 94 | if err != nil { 95 | fmt.Println("Faild. ", err) 96 | } else { 97 | for i, v := range ps { 98 | fmt.Println(i+1, ":", v) 99 | } 100 | } 101 | return 0 102 | } 103 | 104 | //发送消息给在线玩家 105 | func Send(args []string) int { 106 | message := strings.Join(args[1:], " ") 107 | //服务器广播消息 108 | err := centerClient.Broadcast(message) 109 | if err != nil { 110 | fmt.Println("Faild. ", err) 111 | } 112 | 113 | return 0 114 | } 115 | 116 | //获取操作命令map 117 | func GetCommandHandlers() map[string]func(args []string) int { 118 | return map[string]func([]string) int{ 119 | "help": Help, 120 | "h": Help, 121 | "quit": Quit, 122 | "q": Quit, 123 | "login": Login, 124 | "logout": Logout, 125 | "listplayer": ListPlayer, 126 | "send": Send, 127 | } 128 | } 129 | 130 | func main() { 131 | //提示文字 132 | fmt.Println("Casual Game Server Soluion") 133 | 134 | //开始启动服务器 135 | startCenterService() 136 | 137 | //列出帮助指令 138 | Help(nil) 139 | 140 | //定义一个buffer 141 | r := bufio.NewReader(os.Stdin) 142 | 143 | //获取操作指令map 144 | handlers := GetCommandHandlers() 145 | 146 | for { 147 | fmt.Print("command> ") 148 | //循环读取每行指令 149 | b, _, _ := r.ReadLine() 150 | line := string(b) 151 | //分离参数为数组 152 | tokens := strings.Split(line, " ") 153 | //判断是否存在符合要求的handler指令 154 | if handler, ok := handlers[tokens[0]]; ok { 155 | ret := handler(tokens) 156 | if ret != 0 { 157 | break 158 | } 159 | } else { 160 | fmt.Println("Unknown command : ", tokens[0]) 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/ipc/client.go: -------------------------------------------------------------------------------- 1 | //IPC框架的客户端 2 | package ipc 3 | 4 | import ( 5 | "encoding/json" 6 | ) 7 | 8 | type IpcClient struct { //定义ipc客户端内容结构体 9 | conn chan string // conn 为 string 类型的通道 10 | } 11 | 12 | //函数NewIpcClient新建Ipc客户端,传入参数为ipcserver(服务端)结构体,返回为客户端结构体 13 | func NewIpcClient(server *IpcServer) *IpcClient { 14 | c := server.Connect() //连接服务端,返回给c,并给客户端结构体赋值 15 | return &IpcClient{c} 16 | } 17 | 18 | //函数call,是客户端ipcclient定义的client的方法,传入参数为string类型的method和params,返回参数为 *Response类型的resp和error 19 | func (client *IpcClient) Call(method, params string) (resp *Response, err error) { 20 | req := &Request{method, params} //获取request请求结构体,参数为method,params 21 | 22 | var b []byte //定义type类型数组b 23 | b, err = json.Marshal(req) //将req序列化为json格式串 24 | if err != nil { 25 | return 26 | } 27 | client.conn <- string(b) //将json串b传入通道client.conn 28 | str := <-client.conn //获取client.conn通道字符串 29 | 30 | var resp1 Response //定义response类型结构体resp1 31 | err = json.Unmarshal([]byte(str), &resp1) //反序列化str为结构体resp1 32 | resp = &resp1 //将resp1赋值给resp 33 | 34 | return 35 | } 36 | 37 | func (client *IpcClient) Close() { //ipc客户端client定义一个close函数 38 | client.conn <- "CLOSE" //将close字符串扔进client.conn通道 39 | } 40 | -------------------------------------------------------------------------------- /src/ipc/ipc_test.go: -------------------------------------------------------------------------------- 1 | //IPC框架进行单元测试 2 | package ipc 3 | 4 | import ( 5 | "testing" 6 | ) 7 | 8 | type EchoServer struct { //定义一个空结构体EchoServer(输出服务端) 9 | } 10 | 11 | func (server *EchoServer) Handle(request string) string { //函数Handle是结构体server接口的处理函数,传递参数request,响应值string类型 12 | return "ECHO:" + request 13 | } 14 | 15 | func (server *EchoServer) Name() string { //函数Name是结构体server的接口函数,返回值为string类型 16 | return "EchoServer" 17 | } 18 | 19 | func TestIpc(t *testing.T) { //函数testipc,传入参数是testing.T类型的 20 | server := NewIpcServer(&EchoServer{}) // 用NewIpcserver来创建一个server服务端 21 | 22 | client1 := NewIpcClient(server) // 用NewIpcClient来创建两个client客户端 23 | client2 := NewIpcClient(server) 24 | 25 | resp1 := client1.Call("From Client1") //客户端1/2分别输出不同信息,调用call函数 26 | resp2 := client2.Call("From Client2") 27 | 28 | if resp1 != "ECHO:From Client1" || resp2 != "ECHO:From Client2" { //判断返回值是否属于预期 29 | t.Error("IpcClient.Call failed. resp1:", resp1, "resp2:", resp2) //输出错误参数 30 | } 31 | 32 | client1.Close() //关闭2客户端 33 | client2.Close() 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/ipc/server.go: -------------------------------------------------------------------------------- 1 | // IPC框架的服务器端 2 | 3 | package ipc 4 | 5 | import ( 6 | "encoding/json" 7 | "fmt" 8 | ) 9 | 10 | type Request struct { //请求内容结构体 11 | Method string "method" //json字段 12 | Params string "params" 13 | } 14 | 15 | type Response struct { //响应内容结构体 16 | Code string "code" 17 | Body string "body" 18 | } 19 | 20 | type Server interface { //服务端接口 21 | Name() string //名称string 22 | Handle(method, params string) *Response //处理(方法,参数) *Response 23 | } 24 | 25 | type IpcServer struct { //IpcServer服务内容结构体 26 | Server //服务端接口,可以定义多个服务层 27 | } 28 | 29 | func NewIpcServer(server Server) *IpcServer { //创建IpcServer方法,返回值为*ipcserver 30 | return &IpcServer{server} //相当于给IpServer结构体赋值,Server:server 31 | } 32 | 33 | /* 34 | 结构体 server 有一个 方法 connect , 这个方法的返回值 有两个, 一个 是chan 类型, 一个是string 类型 35 | */ 36 | func (server *IpcServer) Connect() chan string { 37 | session := make(chan string, 0) //创建一个session是string类型的通道,长度为0的缓冲区 38 | go func(c chan string) { // 另起轻量级线程实现goroutine,匿名函数,传入参数c,c为string类型的chan通道 39 | for { 40 | request := <-c //从c中取数据到request 41 | if request == "CLOSE" { //判断从通道取到的是否为close 42 | break 43 | } 44 | var req Request //定义req为Request类型 45 | //Unmarshal用于反序列化json的函数 根据data将数据反序列化到传入的对象中 46 | err := json.Unmarshal([]byte(request), &req) //将json反序列化成struct对象 req.method,req.param 47 | if err != nil { 48 | fmt.Println("Invalid request format:", request) 49 | } 50 | resp := server.Handle(req.Method, req.Params) //resp是server方法handle处理后的返回值,类型为*response,req.method,req.params为传入参数 51 | //Marshal 用于将struct对象序列化到json对象中 52 | b, err := json.Marshal(resp) //将resp内容变为b的json串中 53 | c <- string(b) //写入通道c,内容为b的json串 54 | } 55 | fmt.Println("Session closed.") 56 | }(session) 57 | 58 | fmt.Println("A new session has been created successfully.") 59 | return session 60 | } 61 | --------------------------------------------------------------------------------