golang实现STUN协议客户端(RFC 3489和RFC 5389)的插件库go-stun的使用
golang实现STUN协议客户端(RFC 3489和RFC 5389)的插件库go-stun的使用
go-stun是一个用Golang实现的STUN客户端库(支持RFC 3489和RFC 5389),主要用于UDP穿透(NAT穿透)。
使用命令行工具
安装并运行go-stun命令行工具:
go get github.com/ccding/go-stun
go-stun
或者克隆仓库后构建运行:
go build
./go-stun
运行后会输出类似以下结果:
NAT Type: Full cone NAT
External IP Family: 1
External IP: 166.111.4.100
External Port: 23009
命令行参数说明:
-s
指定STUN服务器地址(默认使用stun1.l.google.com:19302)-v
启用详细模式
> ./go-stun --help
Usage of ./go-stun:
-s string
server address (default "stun1.l.google.com:19302")
-v verbose mode
使用库
使用go-stun库非常简单,只需几行代码:
package main
import (
"fmt"
"github.com/ccding/go-stun/stun"
)
func main() {
// 创建STUN客户端并发现NAT类型和外部地址
nat, host, err := stun.NewClient().Discover()
if err != nil {
fmt.Println("Error:", err)
return
}
// 打印结果
fmt.Println("NAT Type:", nat)
fmt.Println("External IP Family:", host.Family())
fmt.Println("External IP:", host.IP())
fmt.Println("External Port:", host.Port())
}
完整示例代码:
package main
import (
"flag"
"fmt"
"log"
"os"
"github.com/ccding/go-stun/stun"
)
func main() {
// 解析命令行参数
server := flag.String("s", "stun1.l.google.com:19302", "STUN server address")
verbose := flag.Bool("v", false, "verbose mode")
flag.Parse()
// 创建STUN客户端
client := stun.NewClient()
client.SetServerAddr(*server)
client.SetVerbose(*verbose)
// 执行发现
nat, host, err := client.Discover()
if err != nil {
log.Fatal(err)
}
// 输出结果
fmt.Fprintf(os.Stdout, "NAT Type: %s\n", nat)
fmt.Fprintf(os.Stdout, "External IP Family: %d\n", host.Family())
fmt.Fprintf(os.Stdout, "External IP: %s\n", host.IP())
fmt.Fprintf(os.Stdout, "External Port: %d\n", host.Port())
}
这个库提供了简单的API来检测NAT类型和获取外部IP地址/端口,非常适合需要实现NAT穿透的P2P应用程序。
更多关于golang实现STUN协议客户端(RFC 3489和RFC 5389)的插件库go-stun的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于golang实现STUN协议客户端(RFC 3489和RFC 5389)的插件库go-stun的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
使用go-stun实现STUN协议客户端
STUN (Session Traversal Utilities for NAT) 是一种用于发现NAT类型和获取公网IP地址/端口的协议。下面我将介绍如何使用go-stun库实现STUN客户端,支持RFC 3489和RFC 5389标准。
安装go-stun库
go get github.com/ccding/go-stun
基本使用示例
1. 最简单的STUN客户端
package main
import (
"fmt"
"log"
"github.com/ccding/go-stun/stun"
)
func main() {
client := stun.NewClient()
client.SetServerAddr("stun.l.google.com:19302") // Google公共STUN服务器
nat, host, err := client.Discover()
if err != nil {
log.Fatal(err)
}
fmt.Printf("NAT Type: %s\n", nat)
fmt.Printf("External IP:port: %s\n", host)
fmt.Printf("External IP: %s\n", host.IP())
fmt.Printf("External Port: %d\n", host.Port())
}
2. 支持RFC 3489和RFC 5389的完整示例
package main
import (
"fmt"
"log"
"net"
"time"
"github.com/ccding/go-stun/stun"
)
func main() {
// 创建STUN客户端
client := stun.NewClient()
// 设置STUN服务器地址 (支持多个RFC标准)
servers := []string{
"stun.l.google.com:19302", // RFC 5389
"stun.xten.com:3478", // RFC 3489
"stun.voipbuster.com:3478", // RFC 3489
}
// 设置超时时间
client.SetTimeout(5 * time.Second)
// 尝试每个STUN服务器
for _, server := range servers {
client.SetServerAddr(server)
fmt.Printf("Testing with server: %s\n", server)
// 发现NAT类型和外部地址
nat, host, err := client.Discover()
if err != nil {
log.Printf("Error with server %s: %v\n", server, err)
continue
}
// 打印结果
fmt.Println("---------------------------------")
fmt.Printf("NAT Type: %s\n", nat)
fmt.Printf("External IP:port: %s\n", host)
fmt.Printf("External IP: %s\n", host.IP())
fmt.Printf("External Port: %d\n", host.Port())
// 获取更多细节 (RFC 5389)
if mappedAddr, changedAddr, err := client.Bind(); err == nil {
fmt.Printf("Mapped Address: %s\n", mappedAddr)
fmt.Printf("Changed Address: %s\n", changedAddr)
}
fmt.Println("---------------------------------")
}
}
3. 自定义STUN请求
package main
import (
"fmt"
"log"
"github.com/ccding/go-stun/stun"
)
func main() {
client := stun.NewClient()
client.SetServerAddr("stun.l.google.com:19302")
// 自定义STUN请求属性
client.SetSoftwareName("MySTUNClient/1.0")
client.SetVerbose(true) // 启用详细日志
// 发送绑定请求
resp, err := client.BindRequest()
if err != nil {
log.Fatal(err)
}
// 解析响应
if resp.HasAttribute(stun.AttrXorMappedAddress) {
xorAddr := resp.GetXorMappedAddress()
fmt.Printf("XOR-Mapped Address: %s\n", xorAddr)
}
if resp.HasAttribute(stun.AttrMappedAddress) {
mappedAddr := resp.GetMappedAddress()
fmt.Printf("Mapped Address: %s\n", mappedAddr)
}
if resp.HasAttribute(stun.AttrChangedAddress) {
changedAddr := resp.GetChangedAddress()
fmt.Printf("Changed Address: %s\n", changedAddr)
}
}
关键功能说明
-
NAT类型检测:
client.Discover()
返回NAT类型,可能是:stun.NATNone
(无NAT)stun.NATFull
(完全锥形NAT)stun.NATRestricted
(受限锥形NAT)stun.NATPortRestricted
(端口受限锥形NAT)stun.NATSymmetric
(对称NAT)
-
获取外部地址:
client.Discover()
返回的外部地址是stun.Host
类型- 可以通过
host.IP()
和host.Port()
分别获取IP和端口
-
RFC 5389特性:
- XOR-MAPPED-ADDRESS属性
- 消息完整性检查
- 指纹属性
高级用法
1. 使用多个STUN服务器进行可靠性检测
func testMultipleServers() {
client := stun.NewClient()
servers := []string{
"stun1.l.google.com:19302",
"stun2.l.google.com:19302",
"stun3.l.google.com:19302",
"stun4.l.google.com:19302",
}
for _, server := range servers {
client.SetServerAddr(server)
nat, host, err := client.Discover()
if err == nil {
fmt.Printf("Consistent result from %s: %s %s\n", server, nat, host)
}
}
}
2. 检测NAT行为特性
func testNATBehavior() {
client := stun.NewClient()
client.SetServerAddr("stun.l.google.com:19302")
// 第一次绑定
nat1, host1, _ := client.Discover()
// 改变本地端口后再次绑定
client.SetLocalAddr(":0") // 随机端口
nat2, host2, _ := client.Discover()
if nat1 == nat2 && host1.Port() == host2.Port() {
fmt.Println("NAT maintains port mapping (no port randomization)")
} else {
fmt.Println("NAT randomizes port mappings")
}
}
注意事项
- STUN服务器可能会限制请求频率,请勿频繁请求
- 某些NAT设备可能会干扰STUN协议,导致检测不准确
- 对于对称NAT,STUN可能无法提供有效的穿透信息
- 在生产环境中,建议使用多个STUN服务器进行交叉验证
go-stun库提供了简单易用的API来实现STUN客户端功能,适用于NAT穿透、WebRTC等场景。通过合理配置和错误处理,可以构建可靠的网络地址发现机制。