add package feature for gudp; gtcp updates

This commit is contained in:
John 2019-04-29 23:54:47 +08:00
parent 7ae03729f3
commit c9537af062
8 changed files with 203 additions and 15 deletions

View File

@ -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 (

View File

@ -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
}
}
}
}

View File

@ -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
}

View File

@ -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
}
}
}
}

115
g/net/gudp/gudp_conn_pkg.go Normal file
View File

@ -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...)
}

View File

@ -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

View File

@ -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...)
}

View File

@ -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")
}