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