TPro协议解析

------

简介：

​	用于对`TPro`的编解码和提供简单的客户端

安装:

```
go get gitlab.dianchu.cc/Taotie/tpro
```

简易客户端示例：(详细可以查看client_example目录)

```go
func main() {
	tProClient, err := tpro.NewClient("127.0.0.1:8080", "user1") // 新建一个客户端，并设置用户名为user1

	if err != nil {
		log.Fatal(err)
	}
	tProClient.SetHandle(serverHandle)                          // 设置服务处理流程
	go tProClient.Listen()                                      // 监听返回信息
	err = tProClient.DoRequest(getCode(), uint32(1), uint16(3)) // 发起一个请求
	if err != nil {
		fmt.Println(err)
	}
	for { // 接收键盘输入是否关闭链接,也可写成接口输入来调用请求
		var s string
		fmt.Scanln(&s)
		if s == "close" {
			tProClient.Close() // 关闭链接
			return
		}
	}
}
func getCode() []byte { // 客户端发起请求前使用protobuf编码请求内容
	response := &HelloHelloRequest{
		Name:                 "user",
		XXX_NoUnkeyedLiteral: struct{}{},
		XXX_unrecognized:     nil,
		XXX_sizecache:        0,
	}
	msg, err := proto.Marshal(response) // 编码
	if err != nil {
		fmt.Println(err)
	}
	return msg
}
func serverHandle(ctx *tpro.ProtoContext) error {
	if ctx.Type == tpro.PTPush {
		switch int(ctx.ProtoID) {
		case 0:
			return nil
		case 1: // 服务端推送protoId为1时
			helloServer(ctx.Msg) // 客户端业务处理流程
			return nil

		default:
			return nil
			// ....
		}
	}
	if ctx.Type == tpro.PTResponse {
		switch int(ctx.SeqId) { // 如果是响应的是根据seqid来的吧
		case 0:
			// ...
			return nil
		}
	}
	return nil
}
func helloServer(msg []byte) { // 客户端处理流程
	request := &HelloHelloResults{}
	var err error
	err = proto.Unmarshal(msg, request) // 解码
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(request.Code, request.Info, request.Data) // 输出服务端段的返回信息
}

```

使用方法:
   编码：

方法一:(获取要发送的信息)

```go
	p := &codec.ProtoContext{
		Header: codec.Header{
			Type:         codec.PTPush,
			IsCompressed: compressed,
			ProtoID:      protoId,
		},
	}
	p.Msg = msg
   msg, err := p.Encode()
	if err != nil {
		return err
	}
```

方法二：

```go
type ioMock struct {
}

func (i *ioMock) Write(p []byte) (n int, err error) {
	return 0, nil
}
stream := &ioMock{}
if err := t.EncodeToStream(stream);err!=nil{//传入一个实现了io.Writer接口的对象
		return err
}
return nil
```

解码：

方法一：

```go
	var p = &codec.ProtoContext{}
	if err := body.Decode(msg); err != nil {//msg为要解码的[]byte
		return err
	}
    
 
```

方法二：

```go
type ioMock struct { //只是示例
	data []byte
	pos  int
}
func (i *ioMock) Read(p []byte) (int, error) {
	l := len(p)
	copy(p[0:l], i.data[i.pos:i.pos+l])
	i.pos += l
	return l, nil
}
 var stream:=&ioMock{} 
 p2, err := NewPContextForSteam(stream) //传入一个实现了io.Read的接口
 if err!=nil{
 	return err
 }
```



推送

```go
if err:=NewPushContext(uint16(16), true, []byte(msg)).EncodeToStream(&ioMock{});err!=nil{
	//log.Print()
}
```



错误解释：`直接看英文翻译吧`

```go
	ErrWrongPackMagic  = errors.New("0xdd proto:package magic header error")
	ErrPackTooShort    = errors.New("0xdd proto:pack length is too short")
	ErrHeadTooShort    = errors.New("0xdd proto:header length is too short")
	ErrBodyTooLong     = errors.New("0xdd proto:body length is too long")
	ErrWrongBodyLength = errors.New("0xdd proto:bodyLength is not find")
	ErrWrongMsgLength  = errors.New("0xdd proto:msg does not match expectations")
```

