| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- package packet
- import (
- "bytes"
- "compress/zlib"
- "errors"
- "fmt"
- "hash/crc32"
- "io"
- "log"
- "mtp20access/utils"
- "net"
- )
- /************协议格式************************************
- WTAS协议是交易系统与管理客户端/会员系统之间的通信协议,由报文头 + 报文体两部分组成
- 标识 类型 字节数 内容 说明
- HeadTag byte 1 0xFF 头标记
- Length uint 4 包总长度,包括头长度
- FunCode uint 4 功能号(为0代表心跳,心跳数据体长度为0)
- SessionId uint 4 会话ID,由交易接入(代理)维护
- Mode byte 1 内容类型,0:ProtoBuff,1:Json,2:Zip + ProtBuff,…
- Version byte 1 版本号
- SerialNumber uint 4 通讯报文序号
- 数据体 业务结构体,ProtoBuf. 数据体格式: 头部 4 节字 + 内容 + 8 节字 掩码, 解密的时候只解内容部分
- CRC byte 4 报文校验和
- FootTag byte 1 0x00 尾标记
- 数据体前,包头长度=19, 空包长度=24
- #define MAXKEY "B0FB83E39A5EBFAABE471362A58393FF"
- #define TRANSKEY "F7A72DE7D6264530F01BA49BC73EB873"
- *******************************************************/
- // MiPacket 协议包结构体
- type MiPacket struct {
- Length uint32 // 包总长度
- FunCode uint32 // 功能能
- SessionId uint32 // 数据包的sid
- Mode uint32 // 业务数据体的格式 0:ProtoBuff,1:Json,2:Zip + ProtoBuff
- SerialNumber uint32 // 通信流水号
- Data []byte // 业务数据体
- CRC uint32 // CRC
- OriMsg []byte // 原始包数据
- }
- // EnPack 打包,根据现有的数据内容重新打包,无任何数据时打出来的是心跳包
- // 这里不对数据体进行加密, 如需加密,请先加密再设置进来
- func (p *MiPacket) EnPack() []byte {
- p.Length = uint32(len(p.Data)) + 24 // 空包长度24
- buf := make([]byte, 0) // 缓冲区
- buf = append(buf, byte(0xFF)) // HeadTag 0xFF
- buf = append(buf, utils.UintToBytes(p.Length)...) // Length
- buf = append(buf, utils.UintToBytes(p.FunCode)...) // FunCode
- buf = append(buf, utils.UintToBytes(p.SessionId)...) // SessionId
- buf = append(buf, byte(0)) // Mode
- buf = append(buf, byte(0)) // Version
- buf = append(buf, utils.UintToBytes(p.SerialNumber)...) // SerialNumber
- buf = append(buf, p.Data...) // 数据体 body
- p.CRC = crc32.Update(58861227, crc32.IEEETable, buf[0:19]) //
- buf = append(buf, utils.UintToBytes(p.CRC)...) // CRC
- buf = append(buf, byte(0)) // FootTag
- return buf
- }
- // UnPackHead 解包头
- // @buf length must be >=19
- func (p *MiPacket) UnPackHead(buf []byte) error {
- if len(buf) < 19 {
- return errors.New("header len error")
- }
- if buf[0] != 0xFF {
- return errors.New("header flag error")
- }
- p.Length = utils.BytesToUint32(buf[1:5]) // Length
- p.FunCode = utils.BytesToUint32(buf[5:9]) // FunCode
- p.SessionId = utils.BytesToUint32(buf[9:13]) // SessionId
- p.SerialNumber = utils.BytesToUint32(buf[15:19]) // SerialNumber
- p.Mode = uint32(buf[13]) // Mode
- if p.Length > 1024*10000 || p.Length < 24 {
- fmt.Println("packet too big or len error, len:", p.Length)
- return fmt.Errorf("invalid len, must in[24,1024000]")
- }
- return nil
- }
- // HeaderLen 包头长度, HeaderTag~SerialNumber 的长度
- // 用于tcp接收数据时第一部分的长度
- func (p *MiPacket) HeaderLen() uint32 {
- return 19
- }
- // BodyLen 除包头外的长度, 含FootTag
- // 用于tcp接收数据时第二部分的长度
- func (p *MiPacket) BodyLen() uint32 {
- return p.Length - 19
- }
- // FuncCode 功能号
- func (p *MiPacket) FuncCode() int {
- return int(p.FunCode)
- }
- // UnPack 从指定的缓冲区解包
- // 注意:不解密业务数据体, 可用 DecodeData 方法进行解密
- // @buf 完整的包数据内容
- func (p *MiPacket) UnPack(buf []byte) error {
- err := p.UnPackHead(buf[:19])
- if err != nil {
- return err
- }
- if len(buf) != int(p.Length) {
- err := fmt.Errorf("packet length err")
- log.Println(err)
- return err
- }
- //长度24为心跳包, 非心跳包时保存数据体内容
- //包头前面19字节+4个字节数据体加密长度, 所以从23开始
- //包尾5个字节,但是加密的数据体后8个字节掩码不需要, 所以是 p.length-13
- if len(buf) != 24 {
- p.Data = buf[23 : p.Length-13]
- }
- p.OriMsg = buf[:]
- return nil
- }
- // DecodeData 解密业务数据体内容
- // @mode 数据体类型 0:ProtoBuff,1:Json,2:Zip + ProtoBuff
- // @msg 业务数据体
- func (p *MiPacket) DecodeData(mode uint32, msg []byte) ([]byte, error) {
- if mode == 2 {
- b := bytes.NewReader(msg)
- r, err := zlib.NewReader(b)
- if err != nil {
- return nil, err
- }
- var out bytes.Buffer
- _, _ = io.Copy(&out, r)
- _ = r.Close()
- msg = out.Bytes()
- }
- return Decrypt(msg, AESKey, true)
- }
- // EncodeData 加密码业务数据体内容
- func (p *MiPacket) EncodeData(mode uint32, msg []byte) ([]byte, error) {
- buf := msg
- if mode == 2 {
- var b bytes.Buffer
- w := zlib.NewWriter(&b)
- _, err := w.Write(msg)
- if err != nil {
- return nil, err
- }
- _ = w.Close()
- buf = b.Bytes()
- }
- return Encrypt(buf, AESKey, true)
- }
- // SetData 设置业务数据, 不含通信包头、CRC等字段
- func (p *MiPacket) SetData(buf []byte) {
- p.Data = buf[:]
- }
- // SetOriMsg 设置原始包数据
- func (p *MiPacket) SetOriMsg(arg ...[]byte) {
- if p.OriMsg == nil {
- p.OriMsg = make([]byte, 0)
- }
- for i := range arg {
- p.OriMsg = append(p.OriMsg, arg[i]...)
- }
- }
- // GetOriMsg 获取解包的原始数据
- func (p *MiPacket) GetOriMsg() []byte {
- return p.OriMsg
- }
- // RebuildForNewSid 设置新的通信流水号(sessionId)且重新打包
- func (p *MiPacket) RebuildForNewSid(sessionId uint32) {
- // 从某个位置开始, 替换一段内容
- f := func(buf []byte, pos int, newBuf []byte) {
- for i := 0; i < len(newBuf); i++ {
- buf[pos] = newBuf[i]
- pos++
- }
- }
- p.SessionId = sessionId
- s := utils.UintToBytes(p.SessionId)
- f(p.OriMsg, 9, s)
- p.CRC = crc32.Update(58861227, crc32.IEEETable, p.OriMsg[0:19])
- crc := utils.UintToBytes(p.CRC)
- f(p.OriMsg, len(p.OriMsg)-5, crc)
- }
- // ReadMessage 从指定tcp链接读取一个协议包
- // @返回值 []byte 未解包的原始数据包, 如果需获取业务数据内容,
- // 请调用UnPack方法后取成员变量 Data 的内容
- func (p *MiPacket) ReadMessage(conn *net.Conn) ([]byte, error) {
- p.OriMsg = make([]byte, 0)
- if conn == nil {
- return p.OriMsg, fmt.Errorf("conn is nil")
- }
- headerBuf := make([]byte, p.HeaderLen())
- nRead, err := io.ReadFull(*conn, headerBuf)
- if err != nil || nRead != len(headerBuf) {
- return p.OriMsg, fmt.Errorf("read header data error, maybe conn closed:%v", err)
- }
- err = p.UnPackHead(headerBuf)
- if err != nil {
- return p.OriMsg, err
- }
- dataBuf := make([]byte, p.BodyLen())
- nRead, err = io.ReadFull(*conn, dataBuf)
- if err != nil || nRead != len(dataBuf) {
- return p.OriMsg, fmt.Errorf("read data error, maybe conn closed:%v", err)
- }
- p.SetOriMsg(headerBuf, dataBuf)
- return p.OriMsg, nil
- }
- // HeaderInfo 头部信息, 功能号、sid、流水号、长度等
- func (p *MiPacket) HeaderInfo() string {
- return fmt.Sprintf("funcode[%d] sid[%d] serial[%d] iLen:%d",
- p.FunCode, p.SessionId, p.SerialNumber, p.Length)
- }
- // BuildPacket 创建包
- // @bCrypto 是否对数据进行加密
- func BuildPacket(funCode, sessionId, serialNum uint32, msg []byte, bCrypto bool) ([]byte, error) {
- p := new(MiPacket)
- p.FunCode = funCode
- p.SessionId = sessionId
- p.SerialNumber = serialNum
- if bCrypto && len(msg) > 0 {
- buf, _ := Encrypt(msg, AESKey, true)
- p.Data = buf
- return p.EnPack(), nil
- }
- p.Data = msg
- return p.EnPack(), nil
- }
|