游戏开发笔记 - 通信协议

游戏开发笔记 - 通信协议

在网络游戏中,通信协议是其中必不可少的组成部分。一个通信协议的大小、结构将与服务器负载及客户端延迟息息相关。

前言游戏的通信协议,一般需要考虑 2 个部分:

网络传输协议

数据交换格式

网络传输协议根据游戏类型的不同,网络传输协议选择也有些许不同。比如:

弱联网类型单工/半双工通信协议:HTTP1.0、HTTP1.1

强联网、强交互类型全双工通信协议:WebSocket、TCP

强联网、实时交互类型UDP

单工、半双工及全双工通信协议的区别用 3 张图来表示一下,具体内容留到下篇学习笔记再水…

单工

半双工

全双工

数据交换格式对于数据交换格式来说,需要解决的最主要的问题就是内容语义清晰,结构稳定,通俗地说不同的程序都可以稳定的解析并且知道怎样去处理它们。

对于数据结构,一般有以下几个选择:

XML可读性高,解析难度高

JSON可读性高、易解析、体积小

Protocol Buffers可读性差、易解析、体积特别小

在确定数据交换协议后,还需要制定数据交换的格式,保证数据不会出现歧义。比如下图这样的格式(未考虑加密)。

从左向右结构:头字节表示协议名称的长度、中间部分是协议名称、最后是协议的具体数据(可以是XML、JSON、PB等)。

实现逻辑使用 WebSocket + PB 来实现一套通信协议,实现一个简易登录协议。

协议文件

1234567891011121314syntax = "proto3";package test;option go_package = "./pb";message C2S_Login { string username = 1; string password = 2;}message S2C_UserInfo { string username = 1; string message = 2;}服务端核心逻辑

123456789101112131415161718192021222324252627282930agent := &Agent{ Id: conn.GetConnID(), Conn: conn,}for { // 读取消息 msg, err := agent.Conn.Receive() if err != nil { break } switch msg.MessageType { case ws.BinaryMessage: cmd, p := packet.Decode(msg.Data) if cmd != "C2S_Login" { fmt.Println("非法协议", cmd) continue } req := &pb.C2S_Login{} proto.Unmarshal(p, req) fmt.Printf("登录用户: %s \n", req.Username) resp := &pb.S2C_UserInfo{ Username: req.Username, Message: "你好, " + req.Username, } agent.Send("S2C_UserInfo", resp) default: fmt.Printf("消息类型不支持: %d \n", msg.MessageType) }}packet/packet.go

123456789101112131415161718192021222324252627282930313233343536373839404142434445package packetimport ( "bytes" "encoding/binary")func Encode(cmd string, p []byte) []byte { buffer := &bytes.Buffer{} // 2字节,协议名称长度 buffer.Write(shortBytes(uint16(len(cmd)))) // 协议名称 buffer.WriteString(cmd) // 协议内容 buffer.Write(p) return buffer.Bytes()}func Decode(p []byte) (string, []byte) { buffer := bytes.NewBuffer(p) // 2字节,协议名称长度 headLen := make([]byte, 2) buffer.Read(headLen) // 协议名称 cmdLen := readShort(headLen) cmd := make([]byte, cmdLen) buffer.Read(cmd) // 协议内容 var bytes []byte if l := buffer.Len(); l > 0 { bytes = make([]byte, l) buffer.Read(bytes) }return string(cmd), bytes}func readShort(b []byte) uint16 { return binary.BigEndian.Uint16(b)}func shortBytes(i uint16) []byte { bytes := make([]byte, 2) binary.BigEndian.PutUint16(bytes, i) return bytes}附上完整实现:protocol-demo

相关数据

GoDaddy教程:将付款方式添加到GoDaddy账户的教程
office365邮箱手机版

GoDaddy教程:将付款方式添加到GoDaddy账户的教程

⌛ 07-18 👁️‍🗨️ 4447
如何查看笔记本电脑尺寸
365bet正网开户

如何查看笔记本电脑尺寸

⌛ 07-25 👁️‍🗨️ 1898
了解BCY软件及其编程功能
365网

了解BCY软件及其编程功能

⌛ 08-05 👁️‍🗨️ 5101