diff --git a/g/encoding/gbinary/gbinary.go b/g/encoding/gbinary/gbinary.go index a7ad3c301..ba44b73a8 100644 --- a/g/encoding/gbinary/gbinary.go +++ b/g/encoding/gbinary/gbinary.go @@ -5,6 +5,8 @@ // You can obtain one at https://github.com/gogf/gf. // Package gbinary provides useful API for handling binary/bytes data. +// +// 注意gbinary模块统一使用LittleEndian进行编码。 package gbinary import ( diff --git a/g/net/gtcp/gtcp_conn.go b/g/net/gtcp/gtcp_conn.go index b1fab08ca..a608c848c 100644 --- a/g/net/gtcp/gtcp_conn.go +++ b/g/net/gtcp/gtcp_conn.go @@ -25,7 +25,8 @@ type Conn struct { } const ( - gRECV_ALL_WAIT_TIMEOUT = time.Millisecond // 读取全部缓冲数据时,没有缓冲数据时的等待间隔 + // 读取全部缓冲数据时,没有缓冲数据时的等待间隔 + gRECV_ALL_WAIT_TIMEOUT = time.Millisecond ) // 创建TCP链接 @@ -104,7 +105,7 @@ func (c *Conn) Recv(length int, retry...Retry) ([]byte, error) { // 缓冲区数据写入等待处理。 // 如果已经读取到数据(这点很关键,表明缓冲区已经有数据,剩下的操作就是将所有数据读取完毕), // 那么可以设置读取全部缓冲数据的超时时间;如果没有接收到任何数据,那么将会进入读取阻塞(或者自定义的超时阻塞); - // 仅对读取全部缓冲数据操作有效 + // 仅对读取全部缓冲区数据操作有效 if length <= 0 && index > 0 { bufferWait = true c.conn.SetReadDeadline(time.Now().Add(c.recvBufferWait)) @@ -118,9 +119,14 @@ func (c *Conn) Recv(length int, retry...Retry) ([]byte, error) { break } } else { - // 如果长度超过了自定义的读取缓冲区,那么自动增长 if index >= gDEFAULT_READ_BUFFER_SIZE { + // 如果长度超过了自定义的读取缓冲区,那么自动增长 buffer = append(buffer, make([]byte, gDEFAULT_READ_BUFFER_SIZE)...) + } else { + // 如果第一次读取的数据并未达到缓冲变量长度,那么直接返回 + if !bufferWait { + break + } } } } diff --git a/g/net/gtcp/gtcp_conn_pkg.go b/g/net/gtcp/gtcp_conn_pkg.go index b23afd7e0..cd0a8ee53 100644 --- a/g/net/gtcp/gtcp_conn_pkg.go +++ b/g/net/gtcp/gtcp_conn_pkg.go @@ -27,14 +27,15 @@ const ( // 2. 由于"总长度"为3字节,并且使用的BigEndian字节序,因此最后返回的buffer使用了buffer[1:]。 func (c *Conn) SendPkg(data []byte, retry...Retry) error { length := uint32(len(data)) - if length - PKG_HEADER_SIZE > PKG_MAX_SIZE { - return errors.New(fmt.Sprintf(`data size %d exceeds max pkg size %d`, length, PKG_MAX_SIZE)) + if length > PKG_MAX_SIZE - PKG_HEADER_SIZE { + return errors.New(fmt.Sprintf(`data size %d exceeds max pkg size %d`, length, PKG_MAX_SIZE - PKG_HEADER_SIZE)) } buffer := make([]byte, PKG_HEADER_SIZE + 1 + len(data)) copy(buffer[PKG_HEADER_SIZE + 1 : ], data) binary.BigEndian.PutUint32(buffer[0 : ], PKG_HEADER_SIZE + length) binary.BigEndian.PutUint32(buffer[4 : ], Checksum(data)) - return c.Send(buffer, retry...) + //fmt.Println("SendPkg:", buffer[1:]) + return c.Send(buffer[1:], retry...) } // 简单协议: 带超时时间的数据发送 @@ -73,7 +74,7 @@ func (c *Conn) RecvPkg(retry...Retry) (result []byte, err error) { // 注意"总长度"为3个字节,不满足4个字节的uint32类型,因此这里"低位"补0 length = binary.BigEndian.Uint32([]byte{0, c.buffer[0], c.buffer[1], c.buffer[2]}) // 解析的大小是否符合规范 - if length == 0 || length - PKG_HEADER_SIZE > PKG_MAX_SIZE { + if length == 0 || length + PKG_HEADER_SIZE > PKG_MAX_SIZE { c.buffer = c.buffer[1:] continue } @@ -101,6 +102,7 @@ func (c *Conn) RecvPkg(retry...Retry) (result []byte, err error) { if len(temp) > 0 { c.buffer = append(c.buffer, temp...) } + //fmt.Println("RecvPkg:", c.buffer) } return } diff --git a/g/net/gudp/gudp_conn.go b/g/net/gudp/gudp_conn.go index e1c751bc7..975ef8c37 100644 --- a/g/net/gudp/gudp_conn.go +++ b/g/net/gudp/gudp_conn.go @@ -14,11 +14,12 @@ import ( // 封装的链接对象 type Conn struct { - conn *net.UDPConn // 底层链接对象 - raddr *net.UDPAddr // 远程地址 - recvDeadline time.Time // 读取超时时间 - sendDeadline time.Time // 写入超时时间 - recvBufferWait time.Duration // 读取全部缓冲区数据时,读取完毕后的写入等待间隔 + conn *net.UDPConn // 底层链接对象 + raddr *net.UDPAddr // 远程地址 + buffer []byte // 读取缓冲区(用于数据读取时的缓冲区处理) + recvDeadline time.Time // 读取超时时间 + sendDeadline time.Time // 写入超时时间 + recvBufferWait time.Duration // 读取全部缓冲区数据时,读取完毕后的写入等待间隔 } const ( @@ -119,9 +120,14 @@ func (c *Conn) Recv(length int, retry...Retry) ([]byte, error) { break } } else { - // 如果长度超过了自定义的读取缓冲区,那么自动增长 if index >= gDEFAULT_READ_BUFFER_SIZE { + // 如果长度超过了自定义的读取缓冲区,那么自动增长 buffer = append(buffer, make([]byte, gDEFAULT_READ_BUFFER_SIZE)...) + } else { + // 如果第一次读取的数据并未达到缓冲变量长度,那么直接返回 + if !bufferWait { + break + } } } } diff --git a/g/net/gudp/gudp_conn_pkg.go b/g/net/gudp/gudp_conn_pkg.go new file mode 100644 index 000000000..4b65be743 --- /dev/null +++ b/g/net/gudp/gudp_conn_pkg.go @@ -0,0 +1,115 @@ +// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gudp + +import ( + "encoding/binary" + "errors" + "fmt" + "time" +) + +const ( + // 允许最大的简单协议包大小(byte), 15MB + PKG_MAX_SIZE = 0xFFFFFF + // 消息包头大小: "总长度"3字节+"校验码"4字节 + PKG_HEADER_SIZE = 7 +) + +// 根据简单协议发送数据包。 +// 简单协议数据格式:总长度(24bit)|校验码(32bit)|数据(变长)。 +// 注意: +// 1. "总长度"包含自身3字节及"校验码"4字节。 +// 2. 由于"总长度"为3字节,并且使用的BigEndian字节序,因此最后返回的buffer使用了buffer[1:]。 +func (c *Conn) SendPkg(data []byte, retry...Retry) error { + length := uint32(len(data)) + if length > PKG_MAX_SIZE - PKG_HEADER_SIZE { + return errors.New(fmt.Sprintf(`data size %d exceeds max pkg size %d`, length, PKG_MAX_SIZE - PKG_HEADER_SIZE)) + } + buffer := make([]byte, PKG_HEADER_SIZE + 1 + len(data)) + copy(buffer[PKG_HEADER_SIZE + 1 : ], data) + binary.BigEndian.PutUint32(buffer[0 : ], PKG_HEADER_SIZE + length) + binary.BigEndian.PutUint32(buffer[4 : ], Checksum(data)) + //fmt.Println("SendPkg:", buffer[1:]) + return c.Send(buffer[1:], retry...) +} + +// 简单协议: 带超时时间的数据发送 +func (c *Conn) SendPkgWithTimeout(data []byte, timeout time.Duration, retry...Retry) error { + c.SetSendDeadline(time.Now().Add(timeout)) + defer c.SetSendDeadline(time.Time{}) + return c.SendPkg(data, retry...) +} + +// 简单协议: 发送数据并等待接收返回数据 +func (c *Conn) SendRecvPkg(data []byte, retry...Retry) ([]byte, error) { + if err := c.SendPkg(data, retry...); err == nil { + return c.RecvPkg(retry...) + } else { + return nil, err + } +} + +// 简单协议: 发送数据并等待接收返回数据(带返回超时等待时间) +func (c *Conn) SendRecvPkgWithTimeout(data []byte, timeout time.Duration, retry...Retry) ([]byte, error) { + if err := c.SendPkg(data, retry...); err == nil { + return c.RecvPkgWithTimeout(timeout, retry...) + } else { + return nil, err + } +} + +// 简单协议: 获取一个数据包。 +func (c *Conn) RecvPkg(retry...Retry) (result []byte, err error) { + var temp []byte + var length uint32 + for { + // 先根据对象的缓冲区数据进行计算 + for { + if len(c.buffer) >= PKG_HEADER_SIZE { + // 注意"总长度"为3个字节,不满足4个字节的uint32类型,因此这里"低位"补0 + length = binary.BigEndian.Uint32([]byte{0, c.buffer[0], c.buffer[1], c.buffer[2]}) + // 解析的大小是否符合规范 + if length == 0 || length + PKG_HEADER_SIZE > PKG_MAX_SIZE { + c.buffer = c.buffer[1:] + continue + } + // 不满足包大小,需要继续读取 + if uint32(len(c.buffer)) < length { + break + } + // 数据校验 + if binary.BigEndian.Uint32(c.buffer[3 : PKG_HEADER_SIZE]) != Checksum(c.buffer[PKG_HEADER_SIZE : length]) { + c.buffer = c.buffer[1:] + continue + } + result = c.buffer[PKG_HEADER_SIZE : length] + c.buffer = c.buffer[length: ] + return + } else { + break + } + } + // 读取系统socket缓冲区的完整数据 + temp, err = c.Recv(-1, retry...) + if err != nil { + break + } + if len(temp) > 0 { + c.buffer = append(c.buffer, temp...) + } + //fmt.Println("RecvPkg:", c.buffer) + } + return +} + +// 简单协议: 带超时时间的消息包获取 +func (c *Conn) RecvPkgWithTimeout(timeout time.Duration, retry...Retry) ([]byte, error) { + c.SetRecvDeadline(time.Now().Add(timeout)) + defer c.SetRecvDeadline(time.Time{}) + return c.RecvPkg(retry...) +} \ No newline at end of file diff --git a/g/net/gudp/gudp_func.go b/g/net/gudp/gudp_func.go index 3d964a528..859832637 100644 --- a/g/net/gudp/gudp_func.go +++ b/g/net/gudp/gudp_func.go @@ -10,6 +10,15 @@ import ( "net" ) +// 常见的二进制数据校验方式,生成校验结果 +func Checksum(buffer []byte) uint32 { + var checksum uint32 + for _, b := range buffer { + checksum += uint32(b) + } + return checksum +} + // 创建标准库UDP链接操作对象 func NewNetConn(raddr string, laddr...string) (*net.UDPConn, error) { var err error diff --git a/g/net/gudp/gudp_func_pkg.go b/g/net/gudp/gudp_func_pkg.go new file mode 100644 index 000000000..f0796064a --- /dev/null +++ b/g/net/gudp/gudp_func_pkg.go @@ -0,0 +1,49 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gudp + +import "time" + +// 简单协议: (面向短链接)发送消息包 +func SendPkg(addr string, data []byte, retry...Retry) error { + conn, err := NewConn(addr) + if err != nil { + return err + } + defer conn.Close() + return conn.SendPkg(data, retry...) +} + +// 简单协议: (面向短链接)发送数据并等待接收返回数据 +func SendRecvPkg(addr string, data []byte, retry...Retry) ([]byte, error) { + conn, err := NewConn(addr) + if err != nil { + return nil, err + } + defer conn.Close() + return conn.SendRecvPkg(data, retry...) +} + +// 简单协议: (面向短链接)带超时时间的数据发送 +func SendPkgWithTimeout(addr string, data []byte, timeout time.Duration, retry...Retry) error { + conn, err := NewConn(addr) + if err != nil { + return err + } + defer conn.Close() + return conn.SendPkgWithTimeout(data, timeout, retry...) +} + +// 简单协议: (面向短链接)发送数据并等待接收返回数据(带返回超时等待时间) +func SendRecvPkgWithTimeout(addr string, data []byte, timeout time.Duration, retry...Retry) ([]byte, error) { + conn, err := NewConn(addr) + if err != nil { + return nil, err + } + defer conn.Close() + return conn.SendRecvPkgWithTimeout(data, timeout, retry...) +} \ No newline at end of file diff --git a/geg/other/test.go b/geg/other/test.go index ac6545942..8cb13c441 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,10 +1,9 @@ package main import ( - "encoding/binary" "fmt" ) func main() { - fmt.Println(binary.BigEndian.Uint32([]byte{byte(1), byte(1), byte(1), byte(1), byte(1)})) + fmt.Println("10" > "4") } \ No newline at end of file