github.com/zhongdalu/gf@v1.0.0/g/net/gtcp/gtcp_conn_pkg.go (about)

     1  // Copyright 2019 gf Author(https://github.com/zhongdalu/gf). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/zhongdalu/gf.
     6  
     7  package gtcp
     8  
     9  import (
    10  	"encoding/binary"
    11  	"fmt"
    12  	"time"
    13  
    14  	"github.com/zhongdalu/gf/g/errors/gerror"
    15  )
    16  
    17  const (
    18  	// 默认允许最大的简单协议包大小(byte), 65535 byte
    19  	gPKG_DEFAULT_MAX_DATA_SIZE = 65535
    20  	// 默认简单协议包头大小
    21  	gPKG_DEFAULT_HEADER_SIZE = 2
    22  	// 协议头最大大小
    23  	gPKG_MAX_HEADER_SIZE = 4
    24  )
    25  
    26  // 数据读取选项
    27  type PkgOption struct {
    28  	HeaderSize  int   // 自定义头大小(默认为2字节,最大不能超过4字节)
    29  	MaxDataSize int   // (byte)数据读取的最大包大小,默认最大不能超过2字节(65535 byte)
    30  	Retry       Retry // 失败重试
    31  }
    32  
    33  // getPkgOption wraps and returns the PkgOption.
    34  // If no option given, it returns a new option with default value.
    35  func getPkgOption(option ...PkgOption) (*PkgOption, error) {
    36  	pkgOption := PkgOption{}
    37  	if len(option) > 0 {
    38  		pkgOption = option[0]
    39  	}
    40  	if pkgOption.HeaderSize == 0 {
    41  		pkgOption.HeaderSize = gPKG_DEFAULT_HEADER_SIZE
    42  	}
    43  	if pkgOption.MaxDataSize == 0 {
    44  		pkgOption.MaxDataSize = gPKG_DEFAULT_MAX_DATA_SIZE
    45  	} else if pkgOption.MaxDataSize > 0xFFFFFF {
    46  		return nil, fmt.Errorf(`package size %d exceeds allowed max size %d`, pkgOption.MaxDataSize, 0xFFFFFF)
    47  	}
    48  	return &pkgOption, nil
    49  }
    50  
    51  // 根据简单协议发送数据包。
    52  //
    53  // 简单协议数据格式:数据长度(24bit)|数据字段(变长)。
    54  //
    55  // 注意:
    56  // 1. "数据长度"仅为"数据字段"的长度,不包含头信息的长度字段3字节。
    57  // 2. 由于"数据长度"为3字节,并且使用的BigEndian字节序,因此这里最后返回的buffer使用了buffer[1:]。
    58  func (c *Conn) SendPkg(data []byte, option ...PkgOption) error {
    59  	pkgOption, err := getPkgOption(option...)
    60  	if err != nil {
    61  		return err
    62  	}
    63  	length := len(data)
    64  	if length > pkgOption.MaxDataSize {
    65  		return fmt.Errorf(`data size %d exceeds max pkg size %d`, length, pkgOption.MaxDataSize)
    66  	}
    67  	offset := gPKG_MAX_HEADER_SIZE - pkgOption.HeaderSize
    68  	buffer := make([]byte, gPKG_MAX_HEADER_SIZE+len(data))
    69  	binary.BigEndian.PutUint32(buffer[0:], uint32(length))
    70  	copy(buffer[gPKG_MAX_HEADER_SIZE:], data)
    71  	if pkgOption.Retry.Count > 0 {
    72  		return c.Send(buffer[offset:], pkgOption.Retry)
    73  	}
    74  	//fmt.Println("SendPkg:", buffer[offset:])
    75  	return c.Send(buffer[offset:])
    76  }
    77  
    78  // 简单协议: 带超时时间的数据发送
    79  func (c *Conn) SendPkgWithTimeout(data []byte, timeout time.Duration, option ...PkgOption) (err error) {
    80  	if err := c.SetSendDeadline(time.Now().Add(timeout)); err != nil {
    81  		return err
    82  	}
    83  	defer func() {
    84  		err = gerror.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
    85  	}()
    86  	err = c.SendPkg(data, option...)
    87  	return
    88  }
    89  
    90  // 简单协议: 发送数据并等待接收返回数据
    91  func (c *Conn) SendRecvPkg(data []byte, option ...PkgOption) ([]byte, error) {
    92  	if err := c.SendPkg(data, option...); err == nil {
    93  		return c.RecvPkg(option...)
    94  	} else {
    95  		return nil, err
    96  	}
    97  }
    98  
    99  // 简单协议: 发送数据并等待接收返回数据(带返回超时等待时间)
   100  func (c *Conn) SendRecvPkgWithTimeout(data []byte, timeout time.Duration, option ...PkgOption) ([]byte, error) {
   101  	if err := c.SendPkg(data, option...); err == nil {
   102  		return c.RecvPkgWithTimeout(timeout, option...)
   103  	} else {
   104  		return nil, err
   105  	}
   106  }
   107  
   108  // 简单协议: 获取一个数据包。
   109  func (c *Conn) RecvPkg(option ...PkgOption) (result []byte, err error) {
   110  	var temp []byte
   111  	var length int
   112  	pkgOption, err := getPkgOption(option...)
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  	for {
   117  		// 先根据对象的缓冲区数据进行计算
   118  		for {
   119  			if len(c.buffer) >= pkgOption.HeaderSize {
   120  				// 不满足4个字节的uint32类型,因此这里"低位"补0
   121  				if length <= 0 {
   122  					switch pkgOption.HeaderSize {
   123  					case 1:
   124  						length = int(binary.BigEndian.Uint32([]byte{0, 0, 0, c.buffer[0]}))
   125  					case 2:
   126  						length = int(binary.BigEndian.Uint32([]byte{0, 0, c.buffer[0], c.buffer[1]}))
   127  					case 3:
   128  						length = int(binary.BigEndian.Uint32([]byte{0, c.buffer[0], c.buffer[1], c.buffer[2]}))
   129  					default:
   130  						length = int(binary.BigEndian.Uint32([]byte{c.buffer[0], c.buffer[1], c.buffer[2], c.buffer[3]}))
   131  					}
   132  				}
   133  				// 解析的大小是否符合规范,清空从该连接接收到的所有数据包
   134  				if length < 0 || length > pkgOption.MaxDataSize {
   135  					c.buffer = c.buffer[:0]
   136  					return nil, fmt.Errorf(`invalid package size %d`, length)
   137  				}
   138  				// 不满足包大小,需要继续读取
   139  				if len(c.buffer) < length+pkgOption.HeaderSize {
   140  					break
   141  				}
   142  				result = c.buffer[pkgOption.HeaderSize : pkgOption.HeaderSize+length]
   143  				c.buffer = c.buffer[pkgOption.HeaderSize+length:]
   144  				length = 0
   145  				return
   146  			} else {
   147  				break
   148  			}
   149  		}
   150  		// 读取系统socket当前缓冲区的数据
   151  		temp, err = c.Recv(0, pkgOption.Retry)
   152  		if err != nil {
   153  			break
   154  		}
   155  		if len(temp) > 0 {
   156  			c.buffer = append(c.buffer, temp...)
   157  		}
   158  		//fmt.Println("RecvPkg:", c.buffer)
   159  	}
   160  	return
   161  }
   162  
   163  // 简单协议: 带超时时间的消息包获取
   164  func (c *Conn) RecvPkgWithTimeout(timeout time.Duration, option ...PkgOption) (data []byte, err error) {
   165  	if err := c.SetRecvDeadline(time.Now().Add(timeout)); err != nil {
   166  		return nil, err
   167  	}
   168  	defer func() {
   169  		err = gerror.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
   170  	}()
   171  	data, err = c.RecvPkg(option...)
   172  	return
   173  }