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

命令行参数说明:

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

关键功能说明

  1. NAT类型检测:

    • client.Discover() 返回NAT类型,可能是:
      • stun.NATNone (无NAT)
      • stun.NATFull (完全锥形NAT)
      • stun.NATRestricted (受限锥形NAT)
      • stun.NATPortRestricted (端口受限锥形NAT)
      • stun.NATSymmetric (对称NAT)
  2. 获取外部地址:

    • client.Discover() 返回的外部地址是stun.Host类型
    • 可以通过host.IP()host.Port()分别获取IP和端口
  3. 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")
	}
}

注意事项

  1. STUN服务器可能会限制请求频率,请勿频繁请求
  2. 某些NAT设备可能会干扰STUN协议,导致检测不准确
  3. 对于对称NAT,STUN可能无法提供有效的穿透信息
  4. 在生产环境中,建议使用多个STUN服务器进行交叉验证

go-stun库提供了简单易用的API来实现STUN客户端功能,适用于NAT穿透、WebRTC等场景。通过合理配置和错误处理,可以构建可靠的网络地址发现机制。

回到顶部