Code Monkey home page Code Monkey logo

go-ratel's Introduction

go-ratel

Ratel 命令行斗地主 golang客户端,完全支持Netty + Protobuf的通信协议,100%兼容Ratel服务端。

开始游戏

  • 1.启动方式一:指定服务器
$ ./ratel -h 39.105.65.8 -p 1024
  • 2.启动方式二:使用可选的服务器
$ ./ratel

视频演示

Ratel 命令行斗地主 golang客户端

关键点剖析

1.使用Google Protobuf - 实现跨平台跨语言的序列化/反序列化

2.Ratel使用Netty内置的Protobuf编解码器

  • 编码器:ProtobufVarint32LengthFieldPrepender
BEFORE DECODE (302 bytes)       AFTER DECODE (300 bytes)
+--------+---------------+      +---------------+
| Length | Protobuf Data |----->| Protobuf Data |
| 0xAC02 |  (300 bytes)  |      |  (300 bytes)  |
+--------+---------------+      +---------------+
  • 解码器:ProtobufVarint32FrameDecoder
BEFORE ENCODE (300 bytes)       AFTER ENCODE (302 bytes)
+---------------+               +--------+---------------+
| Protobuf Data |-------------->| Length | Protobuf Data |
|  (300 bytes)  |               | 0xAC02 |  (300 bytes)  |
+---------------+               +--------+---------------+

大致的协议就是,一个完整包由头部(Header)数据体(Body)组成。编码器会计算出Body的长度放在Header中。Body的长度 采用 Varint,会把整数编码为变长字节。对于 32位整型数据经过Varint编码后需要1~5个字节,小的数字使用1个byte,大的数字使用5个bytes。

3.Golang客户端处理粘包和拆包

  • 编码:序列化结构体,计算数据包长度,换算成Varint
func (c *Codec) Encode(transferData *common.ServerTransferDataProtoc, duration duration.Duration) error {
	// protobuf 序列化
	encodeData, e := proto.Marshal(transferData)
	if e != nil {
		return e
	}
	// 计算数据体长度
	bodyLen := len(encodeData)
	if bodyLen > MaxContextLen {
		return errors.New("not enough")
	}
	// 使用Varint类型
	header := proto.EncodeVarint(uint64(bodyLen))

	buffer := make([]byte, len(header) + bodyLen)
	copy(buffer, header)
	copy(buffer[len(header):], encodeData)

	_, e = c.Conn.Write(buffer)
	return e
}
  • 解码:计算数据包头部,换算Varint,处理粘包,反序列结构体
func (c *Codec) Decode() (*common.ServerTransferDataProtoc, bool, error) {
	// 计算出 body 体长度,以及头部占用的字节数 size
	bodyLen, size := proto.DecodeVarint(c.Buffer.buf[c.Buffer.start:])
	if bodyLen > MaxContextLen {
		return nil, false, errors.New("not enough")
	}
	if bodyLen == 0 {
		return nil, false, nil
	}
	// 在当读取的字节数不满足 body 体的长度,进入下一轮
	body, e := c.Buffer.read(size, int(bodyLen))
	if e != nil {
		return nil, false, nil
	}

	transferData := common.ServerTransferDataProtoc{}
	// 反序列化
	e = proto.Unmarshal(body, &transferData)
	if e != nil {
		return nil, false, e
	}
	return &transferData, true, nil
}

参考资料

1.详解varint编码原理

2.Netty源码分析-ProtobufVarint32FrameDecoder

3.golang 使用 protobuf 的教程

go-ratel's People

Contributors

zuofuhong avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.