You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
naza/pkg/nazanet/avail_udp_conn_pool.go

126 lines
2.9 KiB
Go

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// Copyright 2020, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package nazanet
import (
"net"
"sync"
)
// 从指定的UDP端口范围内寻找可绑定监听的端口绑定监听并返回
// Pool只提供Acquire获取接口不提供释放接口连接资源是标准*net.UDPConn对象需要释放时外部直接Close即可
//
type AvailUdpConnPool struct {
minPort uint16
maxPort uint16
m sync.Mutex
lastPort uint16
}
func NewAvailUdpConnPool(minPort uint16, maxPort uint16) *AvailUdpConnPool {
return &AvailUdpConnPool{
minPort: minPort,
maxPort: maxPort,
lastPort: minPort,
}
}
func (a *AvailUdpConnPool) Acquire() (*net.UDPConn, uint16, error) {
a.m.Lock()
defer a.m.Unlock()
loopFirstFlag := true
p := a.lastPort
for {
// 找了一轮也没有可用的,返回错误
if !loopFirstFlag && p == a.lastPort {
return nil, 0, ErrNazaNet
}
loopFirstFlag = false
conn, err := listenUdpWithPort(p)
// 绑定失败,尝试下一个端口
if err != nil {
p = a.nextPort(p)
continue
}
// 绑定成功更新last返回结果
a.lastPort = a.nextPort(p)
return conn, p, nil
}
}
// 有的业务场景,需要返回两个可用的端口,并且必须是连续的
// @return 前面的是端口小的,后面的是端口+1的
//
func (a *AvailUdpConnPool) Acquire2() (*net.UDPConn, uint16, *net.UDPConn, uint16, error) {
a.m.Lock()
defer a.m.Unlock()
loopFirstFlag := true
p := a.lastPort
for {
// 找了一轮也没有可用的,返回错误
if !loopFirstFlag && p == a.lastPort {
return nil, 0, nil, 0, ErrNazaNet
}
loopFirstFlag = false
// 因为第一个端口如果为最大值,那么和第二个端口肯定不是线性连续了
if p == a.maxPort {
p = a.minPort
continue
}
conn, err := listenUdpWithPort(p)
// 第一个就绑定失败,尝试下一个端口
if err != nil {
p = a.nextPort(p)
continue
}
// 绑定成功,因为我们需要两个,所以我们还要找第二个
// 因为前面已经有判断最大值了,所以直接+1
conn2, err := listenUdpWithPort(p + 1)
// 第二个失败了,关闭第一个,然后从第二个的下一个重新尝试
if err != nil {
_ = conn.Close()
p = a.nextPort(p + 1)
continue
}
// 绑定成功更新last返回结果
a.lastPort = a.nextPort(p + 1)
return conn, p, conn2, p + 1, nil
}
}
// 通过Acquire获取到可用net.UDPConn对象后将对象关闭只返回可用的端口
func (a *AvailUdpConnPool) Peek() (uint16, error) {
conn, port, err := a.Acquire()
if err == nil {
err = conn.Close()
}
return port, err
}
func (a *AvailUdpConnPool) nextPort(p uint16) uint16 {
if p == a.maxPort {
return a.minPort
}
return p + 1
}