1. [feat] package nazabits: 新增函数ReadBits64 2. [feat] package nazanet: UDPConnection支持IPv6 3. [refactor] package nazanet: UDPConnection构造函数支持更多配置

pull/2/head
q191201771 4 years ago
parent 399425c00c
commit f76dce1b46

@ -68,6 +68,7 @@ func (br *BitReader) ReadBits16(n uint) (r uint16, err error) {
return
}
// @param n: 取值范围 [1, 32]
func (br *BitReader) ReadBits32(n uint) (r uint32, err error) {
var t uint8
for i := uint(0); i < n; i++ {
@ -80,6 +81,19 @@ func (br *BitReader) ReadBits32(n uint) (r uint32, err error) {
return
}
// @param n: 取值范围 [1, 64]
func (br *BitReader) ReadBits64(n uint) (r uint64, err error) {
var t uint8
for i := uint(0); i < n; i++ {
t, err = br.ReadBit()
if err != nil {
return
}
r = (r << 1) | uint64(t)
}
return
}
// @param n: 读取多少个字节
func (br *BitReader) ReadBytes(n uint) (r []byte, err error) {
var t uint8

@ -12,4 +12,4 @@ import "errors"
var ErrNazaNet = errors.New("nazanet: fxxk")
const udpNetwork = "udp4"
const udpNetwork = "udp"

@ -13,43 +13,60 @@ import (
"time"
)
const maxReadSizeOfUDPConnection = 1500
// @return 上层回调返回false则关闭UDPConnection
//
type OnReadUDPPacket func(b []byte, raddr *net.UDPAddr, err error) bool
type UDPConnectionOption struct {
// 两种初始化方式:
// 方式一:直接传入外部创建好的连接对象供内部使用
Conn *net.UDPConn
// 方式二:填入地址,内部创建连接对象
// LAddr: 本地bind地址如果设置为空则自动选择可用端口
// 比如作为客户端时,如果不想特别指定本地端口,可以设置为空
//
// Raddr: 如果为空则只能使用func Write2Addr携带对端地址进行发送不能使用func Write
// 不为空的作用:作为客户端时,对端地址通常只有一个,在构造函数中指定,后续就不用每次发送都指定
// 注意对端地址需显示填写IP
//
LAddr string
RAddr string
MaxReadPacketSize int // 读取数据时,内存块大小
AllocEachRead bool // 使用Read Loop时是否每次读取都申请新的内存块如果为false则复用一块内存块
}
var defaultOption = UDPConnectionOption{
MaxReadPacketSize: 1500,
AllocEachRead: true,
}
type UDPConnection struct {
conn *net.UDPConn
option UDPConnectionOption
ruaddr *net.UDPAddr
}
// @param laddr: 本地bind地址如果设置为空则自动选择可用端口
// 比如作为客户端时,如果不想特别指定本地端口,可以设置为空
//
// @param raddr: 如果为空则只能使用func Write2Addr携带对端地址进行发送不能使用func Write
// 好处是作为客户端时,对端地址通常只有一个,在构造函数中指定,后续就不用每次发送都指定
//
func NewUDPConnection(laddr string, raddr string) (c *UDPConnection, err error) {
c = &UDPConnection{}
conn, err := listenUDPWithAddr(laddr)
if err != nil {
return nil, err
type ModUDPConnectionOption func(option *UDPConnectionOption)
func NewUDPConnection(modOptions ...ModUDPConnectionOption) (*UDPConnection, error) {
c := &UDPConnection{}
c.option = defaultOption
for _, fn := range modOptions {
fn(&c.option)
}
if c.option.Conn != nil {
return c, nil
}
c.conn = conn
if c.ruaddr, err = net.ResolveUDPAddr(udpNetwork, raddr); err != nil {
var err error
if c.option.Conn, err = listenUDPWithAddr(c.option.LAddr); err != nil {
return nil, err
}
return c, nil
}
// @param conn: 直接传入外部创建好的连接对象供内部使用
func NewUDPConnectionWithConn(conn *net.UDPConn) (c *UDPConnection) {
return &UDPConnection{
conn: conn,
if c.ruaddr, err = net.ResolveUDPAddr(udpNetwork, c.option.RAddr); err != nil {
return nil, err
}
return c, err
}
// 阻塞直至Read发生错误或上层回调函数返回false
@ -57,10 +74,15 @@ func NewUDPConnectionWithConn(conn *net.UDPConn) (c *UDPConnection) {
// @return error: 如果外部调用Dispose会返回error
//
func (c *UDPConnection) RunLoop(onRead OnReadUDPPacket) error {
// TODO chef: 外部可以指定,是否复用
b := make([]byte, maxReadSizeOfUDPConnection)
var b []byte
if !c.option.AllocEachRead {
b = make([]byte, c.option.MaxReadPacketSize)
}
for {
n, raddr, err := c.conn.ReadFromUDP(b)
if c.option.AllocEachRead {
b = make([]byte, c.option.MaxReadPacketSize)
}
n, raddr, err := c.option.Conn.ReadFromUDP(b)
if keepRunning := onRead(b[:n], raddr, err); !keepRunning {
if err == nil {
return c.Dispose()
@ -76,12 +98,12 @@ func (c *UDPConnection) RunLoop(onRead OnReadUDPPacket) error {
//
func (c *UDPConnection) ReadWithTimeout(timeoutMS int) ([]byte, *net.UDPAddr, error) {
if timeoutMS > 0 {
if err := c.conn.SetReadDeadline(time.Now().Add(time.Duration(timeoutMS) * time.Millisecond)); err != nil {
if err := c.option.Conn.SetReadDeadline(time.Now().Add(time.Duration(timeoutMS) * time.Millisecond)); err != nil {
return nil, nil, err
}
}
b := make([]byte, maxReadSizeOfUDPConnection)
n, raddr, err := c.conn.ReadFromUDP(b)
b := make([]byte, c.option.MaxReadPacketSize)
n, raddr, err := c.option.Conn.ReadFromUDP(b)
if err != nil {
return nil, nil, err
}
@ -89,15 +111,15 @@ func (c *UDPConnection) ReadWithTimeout(timeoutMS int) ([]byte, *net.UDPAddr, er
}
func (c *UDPConnection) Write(b []byte) error {
_, err := c.conn.WriteToUDP(b, c.ruaddr)
_, err := c.option.Conn.WriteToUDP(b, c.ruaddr)
return err
}
func (c *UDPConnection) Write2Addr(b []byte, ruaddr *net.UDPAddr) error {
_, err := c.conn.WriteToUDP(b, ruaddr)
_, err := c.option.Conn.WriteToUDP(b, ruaddr)
return err
}
func (c *UDPConnection) Dispose() error {
return c.conn.Close()
return c.option.Conn.Close()
}

@ -19,12 +19,35 @@ import (
"github.com/q191201771/naza/pkg/nazanet"
)
// [::]:4000 => 0.0.0.0:4000
// [::1]:4000 => 127.0.0.1:4000
//
// ------------------------------
// srv laddr=":4000" raddr=""
// succ:
// cli laddr="" raddr="127.0.0.1:4000"
// cli laddr="" raddr="[::1]:4000"
// cli laddr=":4001" raddr="127.0.0.1:4000"
// fail:
//
// ------------------------------
// srv laddr="[::]:4000" raddr=""
// succ:
// cli laddr="" raddr="[::1]:4000"
// cli laddr="" raddr="127.0.0.1:4000"
// fail:
//
func TestUDPConnection(t *testing.T) {
p := nazanet.NewAvailUDPConnPool(4000, 8000)
srvConn, srvPort, err := p.Acquire()
assert.Equal(t, nil, err)
laddr := fmt.Sprintf(":%d", srvPort)
srv := nazanet.NewUDPConnectionWithConn(srvConn)
toAddr1 := fmt.Sprintf("127.0.0.1:%d", srvPort)
toAddr2 := fmt.Sprintf("[::1]:%d", srvPort)
srv, err := nazanet.NewUDPConnection(func(option *nazanet.UDPConnectionOption) {
option.Conn = srvConn
})
assert.Equal(t, nil, err)
var wg sync.WaitGroup
wg.Add(2)
@ -45,7 +68,9 @@ func TestUDPConnection(t *testing.T) {
assert.IsNotNil(t, err)
}()
cli, err := nazanet.NewUDPConnection("", laddr)
cli, err := nazanet.NewUDPConnection(func(option *nazanet.UDPConnectionOption) {
option.RAddr = toAddr1
})
assert.Equal(t, nil, err)
go func() {
err := cli.Write([]byte("hello"))
@ -58,7 +83,9 @@ func TestUDPConnection(t *testing.T) {
wg.Done()
}()
cli2, err := nazanet.NewUDPConnection("", laddr)
cli2, err := nazanet.NewUDPConnection(func(option *nazanet.UDPConnectionOption) {
option.RAddr = toAddr2
})
assert.Equal(t, nil, err)
go func() {
err := cli2.Write([]byte("hello"))

Loading…
Cancel
Save