mirror of
https://github.com/langhuihui/monibuca.git
synced 2025-09-27 03:25:56 +08:00
feat: add port map to webrtc
This commit is contained in:
133
pkg/port.go
133
pkg/port.go
@@ -13,6 +13,7 @@ type (
|
|||||||
Port struct {
|
Port struct {
|
||||||
Protocol string
|
Protocol string
|
||||||
Ports [2]int
|
Ports [2]int
|
||||||
|
Map [2]int // 映射端口范围,通常用于 NAT 或端口转发
|
||||||
}
|
}
|
||||||
IPort interface {
|
IPort interface {
|
||||||
IsTCP() bool
|
IsTCP() bool
|
||||||
@@ -22,10 +23,23 @@ type (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (p Port) String() string {
|
func (p Port) String() string {
|
||||||
|
var result string
|
||||||
if p.Ports[0] == p.Ports[1] {
|
if p.Ports[0] == p.Ports[1] {
|
||||||
return p.Protocol + ":" + strconv.Itoa(p.Ports[0])
|
result = p.Protocol + ":" + strconv.Itoa(p.Ports[0])
|
||||||
|
} else {
|
||||||
|
result = p.Protocol + ":" + strconv.Itoa(p.Ports[0]) + "-" + strconv.Itoa(p.Ports[1])
|
||||||
}
|
}
|
||||||
return p.Protocol + ":" + strconv.Itoa(p.Ports[0]) + "-" + strconv.Itoa(p.Ports[1])
|
|
||||||
|
// 如果有端口映射,添加映射信息
|
||||||
|
if p.HasMapping() {
|
||||||
|
if p.Map[0] == p.Map[1] {
|
||||||
|
result += ":" + strconv.Itoa(p.Map[0])
|
||||||
|
} else {
|
||||||
|
result += ":" + strconv.Itoa(p.Map[0]) + "-" + strconv.Itoa(p.Map[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Port) IsTCP() bool {
|
func (p Port) IsTCP() bool {
|
||||||
@@ -40,6 +54,36 @@ func (p Port) IsRange() bool {
|
|||||||
return p.Ports[0] != p.Ports[1]
|
return p.Ports[0] != p.Ports[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p Port) HasMapping() bool {
|
||||||
|
return p.Map[0] > 0 || p.Map[1] > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Port) IsRangeMapping() bool {
|
||||||
|
return p.HasMapping() && p.Map[0] != p.Map[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParsePort2 解析端口配置字符串并返回对应的端口类型实例
|
||||||
|
// 根据协议类型和端口范围返回不同的类型:
|
||||||
|
// - TCP单端口:返回 TCPPort
|
||||||
|
// - TCP端口范围:返回 TCPRangePort
|
||||||
|
// - UDP单端口:返回 UDPPort
|
||||||
|
// - UDP端口范围:返回 UDPRangePort
|
||||||
|
//
|
||||||
|
// 参数:
|
||||||
|
//
|
||||||
|
// conf - 端口配置字符串,格式:protocol:port 或 protocol:port1-port2
|
||||||
|
//
|
||||||
|
// 返回值:
|
||||||
|
//
|
||||||
|
// ret - 端口实例 (TCPPort/UDPPort/TCPRangePort/UDPRangePort)
|
||||||
|
// err - 解析错误
|
||||||
|
//
|
||||||
|
// 示例:
|
||||||
|
//
|
||||||
|
// ParsePort2("tcp:8080") // 返回 TCPPort(8080)
|
||||||
|
// ParsePort2("tcp:8080-8090") // 返回 TCPRangePort([2]int{8080, 8090})
|
||||||
|
// ParsePort2("udp:5000") // 返回 UDPPort(5000)
|
||||||
|
// ParsePort2("udp:5000-5010") // 返回 UDPRangePort([2]int{5000, 5010})
|
||||||
func ParsePort2(conf string) (ret any, err error) {
|
func ParsePort2(conf string) (ret any, err error) {
|
||||||
var port Port
|
var port Port
|
||||||
port, err = ParsePort(conf)
|
port, err = ParsePort(conf)
|
||||||
@@ -58,10 +102,84 @@ func ParsePort2(conf string) (ret any, err error) {
|
|||||||
return UDPPort(port.Ports[0]), nil
|
return UDPPort(port.Ports[0]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParsePort 解析端口配置字符串为 Port 结构体
|
||||||
|
// 支持协议前缀、端口号/端口范围以及端口映射的解析
|
||||||
|
//
|
||||||
|
// 参数:
|
||||||
|
//
|
||||||
|
// conf - 端口配置字符串,格式:
|
||||||
|
// - "protocol:port" 单端口,如 "tcp:8080"
|
||||||
|
// - "protocol:port1-port2" 端口范围,如 "tcp:8080-8090"
|
||||||
|
// - "protocol:port:mapPort" 单端口映射,如 "tcp:8080:9090"
|
||||||
|
// - "protocol:port:mapPort1-mapPort2" 单端口映射到端口范围,如 "tcp:8080:9000-9010"
|
||||||
|
// - "protocol:port1-port2:mapPort1-mapPort2" 端口范围映射,如 "tcp:8080-8090:9000-9010"
|
||||||
|
//
|
||||||
|
// 返回值:
|
||||||
|
//
|
||||||
|
// ret - Port 结构体,包含协议、端口和映射端口信息
|
||||||
|
// err - 解析错误
|
||||||
|
//
|
||||||
|
// 注意:
|
||||||
|
// - 如果端口范围中 min > max,会自动交换顺序
|
||||||
|
// - 单端口时,Ports[0] 和 Ports[1] 值相同
|
||||||
|
// - 端口映射时,Map[0] 和 Map[1] 存储映射的目标端口范围
|
||||||
|
// - 单个映射端口时,Map[0] 和 Map[1] 值相同
|
||||||
|
//
|
||||||
|
// 示例:
|
||||||
|
//
|
||||||
|
// ParsePort("tcp:8080") // Port{Protocol:"tcp", Ports:[2]int{8080, 8080}, Map:[2]int{0, 0}}
|
||||||
|
// ParsePort("tcp:8080-8090") // Port{Protocol:"tcp", Ports:[2]int{8080, 8090}, Map:[2]int{0, 0}}
|
||||||
|
// ParsePort("tcp:8080:9090") // Port{Protocol:"tcp", Ports:[2]int{8080, 8080}, Map:[2]int{9090, 9090}}
|
||||||
|
// ParsePort("tcp:8080:9000-9010") // Port{Protocol:"tcp", Ports:[2]int{8080, 8080}, Map:[2]int{9000, 9010}}
|
||||||
|
// ParsePort("tcp:8080-8090:9000-9010") // Port{Protocol:"tcp", Ports:[2]int{8080, 8090}, Map:[2]int{9000, 9010}}
|
||||||
|
// ParsePort("udp:5000") // Port{Protocol:"udp", Ports:[2]int{5000, 5000}, Map:[2]int{0, 0}}
|
||||||
|
// ParsePort("udp:5010-5000") // Port{Protocol:"udp", Ports:[2]int{5000, 5010}, Map:[2]int{0, 0}}
|
||||||
func ParsePort(conf string) (ret Port, err error) {
|
func ParsePort(conf string) (ret Port, err error) {
|
||||||
var port string
|
var port, mapPort string
|
||||||
var min, max int
|
var min, max int
|
||||||
ret.Protocol, port, _ = strings.Cut(conf, ":")
|
|
||||||
|
// 按冒号分割,支持端口映射
|
||||||
|
parts := strings.Split(conf, ":")
|
||||||
|
if len(parts) < 2 || len(parts) > 3 {
|
||||||
|
err = strconv.ErrSyntax
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.Protocol = parts[0]
|
||||||
|
port = parts[1]
|
||||||
|
|
||||||
|
// 处理端口映射
|
||||||
|
if len(parts) == 3 {
|
||||||
|
mapPort = parts[2]
|
||||||
|
// 解析映射端口,支持单端口和端口范围
|
||||||
|
if mapRange := strings.Split(mapPort, "-"); len(mapRange) == 2 {
|
||||||
|
// 映射端口范围
|
||||||
|
var mapMin, mapMax int
|
||||||
|
mapMin, err = strconv.Atoi(mapRange[0])
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mapMax, err = strconv.Atoi(mapRange[1])
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if mapMin < mapMax {
|
||||||
|
ret.Map[0], ret.Map[1] = mapMin, mapMax
|
||||||
|
} else {
|
||||||
|
ret.Map[0], ret.Map[1] = mapMax, mapMin
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 单个映射端口
|
||||||
|
var mapPortNum int
|
||||||
|
mapPortNum, err = strconv.Atoi(mapPort)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret.Map[0], ret.Map[1] = mapPortNum, mapPortNum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理端口范围
|
||||||
if r := strings.Split(port, "-"); len(r) == 2 {
|
if r := strings.Split(port, "-"); len(r) == 2 {
|
||||||
min, err = strconv.Atoi(r[0])
|
min, err = strconv.Atoi(r[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -76,7 +194,12 @@ func ParsePort(conf string) (ret Port, err error) {
|
|||||||
} else {
|
} else {
|
||||||
ret.Ports[0], ret.Ports[1] = max, min
|
ret.Ports[0], ret.Ports[1] = max, min
|
||||||
}
|
}
|
||||||
} else if p, err := strconv.Atoi(port); err == nil {
|
} else {
|
||||||
|
var p int
|
||||||
|
p, err = strconv.Atoi(port)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
ret.Ports[0], ret.Ports[1] = p, p
|
ret.Ports[0], ret.Ports[1] = p, p
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
370
pkg/port_test.go
Normal file
370
pkg/port_test.go
Normal file
@@ -0,0 +1,370 @@
|
|||||||
|
package pkg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParsePort(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
expected Port
|
||||||
|
hasError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "TCP单端口",
|
||||||
|
input: "tcp:8080",
|
||||||
|
expected: Port{
|
||||||
|
Protocol: "tcp",
|
||||||
|
Ports: [2]int{8080, 8080},
|
||||||
|
Map: [2]int{0, 0},
|
||||||
|
},
|
||||||
|
hasError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TCP端口范围",
|
||||||
|
input: "tcp:8080-8090",
|
||||||
|
expected: Port{
|
||||||
|
Protocol: "tcp",
|
||||||
|
Ports: [2]int{8080, 8090},
|
||||||
|
Map: [2]int{0, 0},
|
||||||
|
},
|
||||||
|
hasError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TCP端口范围(反序)",
|
||||||
|
input: "tcp:8090-8080",
|
||||||
|
expected: Port{
|
||||||
|
Protocol: "tcp",
|
||||||
|
Ports: [2]int{8080, 8090},
|
||||||
|
Map: [2]int{0, 0},
|
||||||
|
},
|
||||||
|
hasError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TCP单端口映射到单端口",
|
||||||
|
input: "tcp:8080:9090",
|
||||||
|
expected: Port{
|
||||||
|
Protocol: "tcp",
|
||||||
|
Ports: [2]int{8080, 8080},
|
||||||
|
Map: [2]int{9090, 9090},
|
||||||
|
},
|
||||||
|
hasError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TCP单端口映射到端口范围",
|
||||||
|
input: "tcp:8080:9000-9010",
|
||||||
|
expected: Port{
|
||||||
|
Protocol: "tcp",
|
||||||
|
Ports: [2]int{8080, 8080},
|
||||||
|
Map: [2]int{9000, 9010},
|
||||||
|
},
|
||||||
|
hasError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TCP端口范围映射到端口范围",
|
||||||
|
input: "tcp:8080-8090:9000-9010",
|
||||||
|
expected: Port{
|
||||||
|
Protocol: "tcp",
|
||||||
|
Ports: [2]int{8080, 8090},
|
||||||
|
Map: [2]int{9000, 9010},
|
||||||
|
},
|
||||||
|
hasError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "UDP单端口",
|
||||||
|
input: "udp:5000",
|
||||||
|
expected: Port{
|
||||||
|
Protocol: "udp",
|
||||||
|
Ports: [2]int{5000, 5000},
|
||||||
|
Map: [2]int{0, 0},
|
||||||
|
},
|
||||||
|
hasError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "UDP端口范围",
|
||||||
|
input: "udp:5000-5010",
|
||||||
|
expected: Port{
|
||||||
|
Protocol: "udp",
|
||||||
|
Ports: [2]int{5000, 5010},
|
||||||
|
Map: [2]int{0, 0},
|
||||||
|
},
|
||||||
|
hasError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "UDP端口映射",
|
||||||
|
input: "udp:5000:6000",
|
||||||
|
expected: Port{
|
||||||
|
Protocol: "udp",
|
||||||
|
Ports: [2]int{5000, 5000},
|
||||||
|
Map: [2]int{6000, 6000},
|
||||||
|
},
|
||||||
|
hasError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "UDP端口范围映射(映射范围反序)",
|
||||||
|
input: "udp:5000-5010:6010-6000",
|
||||||
|
expected: Port{
|
||||||
|
Protocol: "udp",
|
||||||
|
Ports: [2]int{5000, 5010},
|
||||||
|
Map: [2]int{6000, 6010},
|
||||||
|
},
|
||||||
|
hasError: false,
|
||||||
|
},
|
||||||
|
// 错误情况
|
||||||
|
{
|
||||||
|
name: "缺少协议",
|
||||||
|
input: "8080",
|
||||||
|
expected: Port{},
|
||||||
|
hasError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "过多冒号",
|
||||||
|
input: "tcp:8080:9090:extra",
|
||||||
|
expected: Port{},
|
||||||
|
hasError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "无效端口号",
|
||||||
|
input: "tcp:abc",
|
||||||
|
expected: Port{},
|
||||||
|
hasError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "无效映射端口号",
|
||||||
|
input: "tcp:8080:abc",
|
||||||
|
expected: Port{},
|
||||||
|
hasError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "无效端口范围",
|
||||||
|
input: "tcp:8080-abc",
|
||||||
|
expected: Port{},
|
||||||
|
hasError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "无效映射端口范围",
|
||||||
|
input: "tcp:8080:9000-abc",
|
||||||
|
expected: Port{},
|
||||||
|
hasError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result, err := ParsePort(tt.input)
|
||||||
|
|
||||||
|
if tt.hasError {
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("期望有错误,但没有错误")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("意外的错误: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.Protocol != tt.expected.Protocol {
|
||||||
|
t.Errorf("协议不匹配: 期望 %s, 得到 %s", tt.expected.Protocol, result.Protocol)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.Ports != tt.expected.Ports {
|
||||||
|
t.Errorf("端口不匹配: 期望 %v, 得到 %v", tt.expected.Ports, result.Ports)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.Map != tt.expected.Map {
|
||||||
|
t.Errorf("映射端口不匹配: 期望 %v, 得到 %v", tt.expected.Map, result.Map)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPortMethods(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
port Port
|
||||||
|
expectTCP bool
|
||||||
|
expectUDP bool
|
||||||
|
expectRange bool
|
||||||
|
expectMapping bool
|
||||||
|
expectRangeMap bool
|
||||||
|
expectString string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "TCP单端口",
|
||||||
|
port: Port{
|
||||||
|
Protocol: "tcp",
|
||||||
|
Ports: [2]int{8080, 8080},
|
||||||
|
Map: [2]int{0, 0},
|
||||||
|
},
|
||||||
|
expectTCP: true,
|
||||||
|
expectUDP: false,
|
||||||
|
expectRange: false,
|
||||||
|
expectMapping: false,
|
||||||
|
expectRangeMap: false,
|
||||||
|
expectString: "tcp:8080",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TCP端口范围",
|
||||||
|
port: Port{
|
||||||
|
Protocol: "tcp",
|
||||||
|
Ports: [2]int{8080, 8090},
|
||||||
|
Map: [2]int{0, 0},
|
||||||
|
},
|
||||||
|
expectTCP: true,
|
||||||
|
expectUDP: false,
|
||||||
|
expectRange: true,
|
||||||
|
expectMapping: false,
|
||||||
|
expectRangeMap: false,
|
||||||
|
expectString: "tcp:8080-8090",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TCP单端口映射",
|
||||||
|
port: Port{
|
||||||
|
Protocol: "tcp",
|
||||||
|
Ports: [2]int{8080, 8080},
|
||||||
|
Map: [2]int{9090, 9090},
|
||||||
|
},
|
||||||
|
expectTCP: true,
|
||||||
|
expectUDP: false,
|
||||||
|
expectRange: false,
|
||||||
|
expectMapping: true,
|
||||||
|
expectRangeMap: false,
|
||||||
|
expectString: "tcp:8080:9090",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TCP端口范围映射",
|
||||||
|
port: Port{
|
||||||
|
Protocol: "tcp",
|
||||||
|
Ports: [2]int{8080, 8090},
|
||||||
|
Map: [2]int{9000, 9010},
|
||||||
|
},
|
||||||
|
expectTCP: true,
|
||||||
|
expectUDP: false,
|
||||||
|
expectRange: true,
|
||||||
|
expectMapping: true,
|
||||||
|
expectRangeMap: true,
|
||||||
|
expectString: "tcp:8080-8090:9000-9010",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "UDP单端口映射到端口范围",
|
||||||
|
port: Port{
|
||||||
|
Protocol: "udp",
|
||||||
|
Ports: [2]int{5000, 5000},
|
||||||
|
Map: [2]int{6000, 6010},
|
||||||
|
},
|
||||||
|
expectTCP: false,
|
||||||
|
expectUDP: true,
|
||||||
|
expectRange: false,
|
||||||
|
expectMapping: true,
|
||||||
|
expectRangeMap: true,
|
||||||
|
expectString: "udp:5000:6000-6010",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if tt.port.IsTCP() != tt.expectTCP {
|
||||||
|
t.Errorf("IsTCP(): 期望 %v, 得到 %v", tt.expectTCP, tt.port.IsTCP())
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.port.IsUDP() != tt.expectUDP {
|
||||||
|
t.Errorf("IsUDP(): 期望 %v, 得到 %v", tt.expectUDP, tt.port.IsUDP())
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.port.IsRange() != tt.expectRange {
|
||||||
|
t.Errorf("IsRange(): 期望 %v, 得到 %v", tt.expectRange, tt.port.IsRange())
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.port.HasMapping() != tt.expectMapping {
|
||||||
|
t.Errorf("HasMapping(): 期望 %v, 得到 %v", tt.expectMapping, tt.port.HasMapping())
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.port.IsRangeMapping() != tt.expectRangeMap {
|
||||||
|
t.Errorf("IsRangeMapping(): 期望 %v, 得到 %v", tt.expectRangeMap, tt.port.IsRangeMapping())
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.port.String() != tt.expectString {
|
||||||
|
t.Errorf("String(): 期望 %s, 得到 %s", tt.expectString, tt.port.String())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParsePort2(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
expectedType string
|
||||||
|
hasError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "TCP单端口",
|
||||||
|
input: "tcp:8080",
|
||||||
|
expectedType: "TCPPort",
|
||||||
|
hasError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TCP端口范围",
|
||||||
|
input: "tcp:8080-8090",
|
||||||
|
expectedType: "TCPRangePort",
|
||||||
|
hasError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "UDP单端口",
|
||||||
|
input: "udp:5000",
|
||||||
|
expectedType: "UDPPort",
|
||||||
|
hasError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "UDP端口范围",
|
||||||
|
input: "udp:5000-5010",
|
||||||
|
expectedType: "UDPRangePort",
|
||||||
|
hasError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "无效输入",
|
||||||
|
input: "invalid",
|
||||||
|
hasError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result, err := ParsePort2(tt.input)
|
||||||
|
|
||||||
|
if tt.hasError {
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("期望有错误,但没有错误")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("意外的错误: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tt.expectedType {
|
||||||
|
case "TCPPort":
|
||||||
|
if _, ok := result.(TCPPort); !ok {
|
||||||
|
t.Errorf("期望类型 TCPPort, 得到 %T", result)
|
||||||
|
}
|
||||||
|
case "TCPRangePort":
|
||||||
|
if _, ok := result.(TCPRangePort); !ok {
|
||||||
|
t.Errorf("期望类型 TCPRangePort, 得到 %T", result)
|
||||||
|
}
|
||||||
|
case "UDPPort":
|
||||||
|
if _, ok := result.(UDPPort); !ok {
|
||||||
|
t.Errorf("期望类型 UDPPort, 得到 %T", result)
|
||||||
|
}
|
||||||
|
case "UDPRangePort":
|
||||||
|
if _, ok := result.(UDPRangePort); !ok {
|
||||||
|
t.Errorf("期望类型 UDPRangePort, 得到 %T", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@@ -43,6 +43,7 @@ type WebRTCPlugin struct {
|
|||||||
EnableDC bool `default:"true" desc:"是否启用DataChannel"` // 在不支持编码格式的情况下是否启用DataChannel传输
|
EnableDC bool `default:"true" desc:"是否启用DataChannel"` // 在不支持编码格式的情况下是否启用DataChannel传输
|
||||||
MimeType []string `desc:"MimeType过滤列表,为空则不过滤"` // MimeType过滤列表,支持的格式如:video/H264, audio/opus
|
MimeType []string `desc:"MimeType过滤列表,为空则不过滤"` // MimeType过滤列表,支持的格式如:video/H264, audio/opus
|
||||||
s SettingEngine
|
s SettingEngine
|
||||||
|
portMapping map[int]int // 内部端口到外部端口的映射
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *WebRTCPlugin) RegisterHandler() map[string]http.HandlerFunc {
|
func (p *WebRTCPlugin) RegisterHandler() map[string]http.HandlerFunc {
|
||||||
@@ -306,15 +307,48 @@ func (p *WebRTCPlugin) initSettingEngine() error {
|
|||||||
|
|
||||||
// configurePort 配置端口设置
|
// configurePort 配置端口设置
|
||||||
func (p *WebRTCPlugin) configurePort() error {
|
func (p *WebRTCPlugin) configurePort() error {
|
||||||
ports, err := ParsePort2(p.Port)
|
// 使用 ParsePort 而不是 ParsePort2 来获取端口映射信息
|
||||||
|
portInfo, err := ParsePort(p.Port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.Error("webrtc port config error", "error", err, "port", p.Port)
|
p.Error("webrtc port config error", "error", err, "port", p.Port)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
switch v := ports.(type) {
|
// 初始化端口映射
|
||||||
case TCPPort:
|
p.portMapping = make(map[int]int)
|
||||||
tcpport := int(v)
|
|
||||||
|
// 如果有端口映射,存储映射关系
|
||||||
|
if portInfo.HasMapping() {
|
||||||
|
if portInfo.IsRange() {
|
||||||
|
// 端口范围映射
|
||||||
|
for i := 0; i <= portInfo.Ports[1]-portInfo.Ports[0]; i++ {
|
||||||
|
internalPort := portInfo.Ports[0] + i
|
||||||
|
var externalPort int
|
||||||
|
if portInfo.IsRangeMapping() {
|
||||||
|
// 映射端口也是范围
|
||||||
|
externalPort = portInfo.Map[0] + i
|
||||||
|
} else {
|
||||||
|
// 映射端口是单个端口
|
||||||
|
externalPort = portInfo.Map[0]
|
||||||
|
}
|
||||||
|
p.portMapping[internalPort] = externalPort
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 单端口映射
|
||||||
|
p.portMapping[portInfo.Ports[0]] = portInfo.Map[0]
|
||||||
|
}
|
||||||
|
p.Info("Port mapping configured", "mapping", p.portMapping)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据协议类型进行配置
|
||||||
|
if portInfo.IsTCP() {
|
||||||
|
if portInfo.IsRange() {
|
||||||
|
// TCP端口范围,这里可能需要特殊处理
|
||||||
|
p.Error("TCP port range not supported in current implementation")
|
||||||
|
return fmt.Errorf("TCP port range not supported")
|
||||||
|
} else {
|
||||||
|
// TCP单端口
|
||||||
|
tcpport := portInfo.Ports[0]
|
||||||
tcpl, err := net.ListenTCP("tcp", &net.TCPAddr{
|
tcpl, err := net.ListenTCP("tcp", &net.TCPAddr{
|
||||||
IP: net.IP{0, 0, 0, 0},
|
IP: net.IP{0, 0, 0, 0},
|
||||||
Port: tcpport,
|
Port: tcpport,
|
||||||
@@ -324,20 +358,26 @@ func (p *WebRTCPlugin) configurePort() error {
|
|||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.Error("webrtc listener tcp", "error", err)
|
p.Error("webrtc listener tcp", "error", err)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
p.SetDescription("tcp", fmt.Sprintf("%d", tcpport))
|
p.SetDescription("tcp", fmt.Sprintf("%d", tcpport))
|
||||||
p.Info("webrtc start listen", "port", tcpport)
|
p.Info("webrtc start listen", "port", tcpport)
|
||||||
p.s.SetICETCPMux(NewICETCPMux(nil, tcpl, 4096))
|
p.s.SetICETCPMux(NewICETCPMux(nil, tcpl, 4096))
|
||||||
p.s.SetNetworkTypes([]NetworkType{NetworkTypeTCP4, NetworkTypeTCP6})
|
p.s.SetNetworkTypes([]NetworkType{NetworkTypeTCP4, NetworkTypeTCP6})
|
||||||
p.s.DisableSRTPReplayProtection(true)
|
p.s.DisableSRTPReplayProtection(true)
|
||||||
case UDPRangePort:
|
}
|
||||||
p.s.SetEphemeralUDPPortRange(uint16(v[0]), uint16(v[1]))
|
} else {
|
||||||
p.SetDescription("udp", fmt.Sprintf("%d-%d", v[0], v[1]))
|
// UDP配置
|
||||||
case UDPPort:
|
if portInfo.IsRange() {
|
||||||
// 创建共享WEBRTC端口 默认9000
|
// UDP端口范围
|
||||||
|
p.s.SetEphemeralUDPPortRange(uint16(portInfo.Ports[0]), uint16(portInfo.Ports[1]))
|
||||||
|
p.SetDescription("udp", fmt.Sprintf("%d-%d", portInfo.Ports[0], portInfo.Ports[1]))
|
||||||
|
} else {
|
||||||
|
// UDP单端口
|
||||||
|
udpport := portInfo.Ports[0]
|
||||||
udpListener, err := net.ListenUDP("udp", &net.UDPAddr{
|
udpListener, err := net.ListenUDP("udp", &net.UDPAddr{
|
||||||
IP: net.IP{0, 0, 0, 0},
|
IP: net.IP{0, 0, 0, 0},
|
||||||
Port: int(v),
|
Port: udpport,
|
||||||
})
|
})
|
||||||
p.OnDispose(func() {
|
p.OnDispose(func() {
|
||||||
_ = udpListener.Close()
|
_ = udpListener.Close()
|
||||||
@@ -346,11 +386,12 @@ func (p *WebRTCPlugin) configurePort() error {
|
|||||||
p.Error("webrtc listener udp", "error", err)
|
p.Error("webrtc listener udp", "error", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
p.SetDescription("udp", fmt.Sprintf("%d", v))
|
p.SetDescription("udp", fmt.Sprintf("%d", udpport))
|
||||||
p.Info("webrtc start listen", "port", v)
|
p.Info("webrtc start listen", "port", udpport)
|
||||||
p.s.SetICEUDPMux(NewICEUDPMux(nil, udpListener))
|
p.s.SetICEUDPMux(NewICEUDPMux(nil, udpListener))
|
||||||
p.s.SetNetworkTypes([]NetworkType{NetworkTypeUDP4, NetworkTypeUDP6})
|
p.s.SetNetworkTypes([]NetworkType{NetworkTypeUDP4, NetworkTypeUDP6})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -368,9 +409,33 @@ func (p *WebRTCPlugin) CreatePC(sd SessionDescription, conf Configuration) (pc *
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
pc, err = api.NewPeerConnection(conf)
|
pc, err = api.NewPeerConnection(conf)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
err = pc.SetRemoteDescription(sd)
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果有端口映射配置,记录 ICE 候选者信息以供调试
|
||||||
|
if len(p.portMapping) > 0 {
|
||||||
|
pc.OnICECandidate(func(candidate *ICECandidate) {
|
||||||
|
if candidate != nil {
|
||||||
|
// 记录端口映射信息(用于调试和监控)
|
||||||
|
if mappedPort, exists := p.portMapping[int(candidate.Port)]; exists {
|
||||||
|
p.Debug("ICE candidate with port mapping detected",
|
||||||
|
"original_port", candidate.Port,
|
||||||
|
"mapped_port", mappedPort,
|
||||||
|
"candidate_address", candidate.Address,
|
||||||
|
"candidate_type", candidate.Typ)
|
||||||
|
candidate.Port = uint16(mappedPort) // 更新候选者端口为映射后的端口
|
||||||
|
} else {
|
||||||
|
p.Debug("ICE candidate generated",
|
||||||
|
"port", candidate.Port,
|
||||||
|
"address", candidate.Address,
|
||||||
|
"type", candidate.Typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
err = pc.SetRemoteDescription(sd)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user