github.com/gogf/gf@v1.16.9/net/gtcp/gtcp_conn_pkg.go (about) 1 // Copyright GoFrame Author(https://goframe.org). 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/gogf/gf. 6 7 package gtcp 8 9 import ( 10 "encoding/binary" 11 "fmt" 12 "time" 13 ) 14 15 const ( 16 pkgHeaderSizeDefault = 2 // Header size for simple package protocol. 17 pkgHeaderSizeMax = 4 // Max header size for simple package protocol. 18 ) 19 20 // PkgOption is package option for simple protocol. 21 type PkgOption struct { 22 // HeaderSize is used to mark the data length for next data receiving. 23 // It's 2 bytes in default, 4 bytes max, which stands for the max data length 24 // from 65535 to 4294967295 bytes. 25 HeaderSize int 26 27 // MaxDataSize is the data field size in bytes for data length validation. 28 // If it's not manually set, it'll automatically be set correspondingly with the HeaderSize. 29 MaxDataSize int 30 31 // Retry policy when operation fails. 32 Retry Retry 33 } 34 35 // SendPkg send data using simple package protocol. 36 // 37 // Simple package protocol: DataLength(24bit)|DataField(variant)。 38 // 39 // Note that, 40 // 1. The DataLength is the length of DataField, which does not contain the header size. 41 // 2. The integer bytes of the package are encoded using BigEndian order. 42 func (c *Conn) SendPkg(data []byte, option ...PkgOption) error { 43 pkgOption, err := getPkgOption(option...) 44 if err != nil { 45 return err 46 } 47 length := len(data) 48 if length > pkgOption.MaxDataSize { 49 return fmt.Errorf( 50 `data too long, data size %d exceeds allowed max data size %d`, 51 length, pkgOption.MaxDataSize, 52 ) 53 } 54 offset := pkgHeaderSizeMax - pkgOption.HeaderSize 55 buffer := make([]byte, pkgHeaderSizeMax+len(data)) 56 binary.BigEndian.PutUint32(buffer[0:], uint32(length)) 57 copy(buffer[pkgHeaderSizeMax:], data) 58 if pkgOption.Retry.Count > 0 { 59 return c.Send(buffer[offset:], pkgOption.Retry) 60 } 61 return c.Send(buffer[offset:]) 62 } 63 64 // SendPkgWithTimeout writes data to connection with timeout using simple package protocol. 65 func (c *Conn) SendPkgWithTimeout(data []byte, timeout time.Duration, option ...PkgOption) (err error) { 66 if err := c.SetSendDeadline(time.Now().Add(timeout)); err != nil { 67 return err 68 } 69 defer c.SetSendDeadline(time.Time{}) 70 err = c.SendPkg(data, option...) 71 return 72 } 73 74 // SendRecvPkg writes data to connection and blocks reading response using simple package protocol. 75 func (c *Conn) SendRecvPkg(data []byte, option ...PkgOption) ([]byte, error) { 76 if err := c.SendPkg(data, option...); err == nil { 77 return c.RecvPkg(option...) 78 } else { 79 return nil, err 80 } 81 } 82 83 // SendRecvPkgWithTimeout writes data to connection and reads response with timeout using simple package protocol. 84 func (c *Conn) SendRecvPkgWithTimeout(data []byte, timeout time.Duration, option ...PkgOption) ([]byte, error) { 85 if err := c.SendPkg(data, option...); err == nil { 86 return c.RecvPkgWithTimeout(timeout, option...) 87 } else { 88 return nil, err 89 } 90 } 91 92 // RecvPkg receives data from connection using simple package protocol. 93 func (c *Conn) RecvPkg(option ...PkgOption) (result []byte, err error) { 94 var buffer []byte 95 var length int 96 pkgOption, err := getPkgOption(option...) 97 if err != nil { 98 return nil, err 99 } 100 // Header field. 101 buffer, err = c.Recv(pkgOption.HeaderSize, pkgOption.Retry) 102 if err != nil { 103 return nil, err 104 } 105 switch pkgOption.HeaderSize { 106 case 1: 107 // It fills with zero if the header size is lesser than 4 bytes (uint32). 108 length = int(binary.BigEndian.Uint32([]byte{0, 0, 0, buffer[0]})) 109 case 2: 110 length = int(binary.BigEndian.Uint32([]byte{0, 0, buffer[0], buffer[1]})) 111 case 3: 112 length = int(binary.BigEndian.Uint32([]byte{0, buffer[0], buffer[1], buffer[2]})) 113 default: 114 length = int(binary.BigEndian.Uint32([]byte{buffer[0], buffer[1], buffer[2], buffer[3]})) 115 } 116 // It here validates the size of the package. 117 // It clears the buffer and returns error immediately if it validates failed. 118 if length < 0 || length > pkgOption.MaxDataSize { 119 return nil, fmt.Errorf(`invalid package size %d`, length) 120 } 121 // Empty package. 122 if length == 0 { 123 return nil, nil 124 } 125 // Data field. 126 return c.Recv(length, pkgOption.Retry) 127 } 128 129 // RecvPkgWithTimeout reads data from connection with timeout using simple package protocol. 130 func (c *Conn) RecvPkgWithTimeout(timeout time.Duration, option ...PkgOption) (data []byte, err error) { 131 if err := c.SetreceiveDeadline(time.Now().Add(timeout)); err != nil { 132 return nil, err 133 } 134 defer c.SetreceiveDeadline(time.Time{}) 135 data, err = c.RecvPkg(option...) 136 return 137 } 138 139 // getPkgOption wraps and returns the PkgOption. 140 // If no option given, it returns a new option with default value. 141 func getPkgOption(option ...PkgOption) (*PkgOption, error) { 142 pkgOption := PkgOption{} 143 if len(option) > 0 { 144 pkgOption = option[0] 145 } 146 if pkgOption.HeaderSize == 0 { 147 pkgOption.HeaderSize = pkgHeaderSizeDefault 148 } 149 if pkgOption.HeaderSize > pkgHeaderSizeMax { 150 return nil, fmt.Errorf( 151 `package header size %d definition exceeds max header size %d`, 152 pkgOption.HeaderSize, pkgHeaderSizeMax, 153 ) 154 } 155 if pkgOption.MaxDataSize == 0 { 156 switch pkgOption.HeaderSize { 157 case 1: 158 pkgOption.MaxDataSize = 0xFF 159 case 2: 160 pkgOption.MaxDataSize = 0xFFFF 161 case 3: 162 pkgOption.MaxDataSize = 0xFFFFFF 163 case 4: 164 // math.MaxInt32 not math.MaxUint32 165 pkgOption.MaxDataSize = 0x7FFFFFFF 166 } 167 } 168 if pkgOption.MaxDataSize > 0x7FFFFFFF { 169 return nil, fmt.Errorf( 170 `package data size %d definition exceeds allowed max data size %d`, 171 pkgOption.MaxDataSize, 0x7FFFFFFF, 172 ) 173 } 174 return &pkgOption, nil 175 }