Go语言使用UDP做一个NAT打洞
首先需要一个公网服务器用来做转发,运行server服务
package main
import (
"fmt"
"net"
"sync"
)
var clients = make(map[string]*net.UDPAddr)
var mu sync.Mutex
func main() {
addr, err := net.ResolveUDPAddr("udp6", ":8172")
if err != nil {
fmt.Println("Error resolving address:", err)
return
}
fmt.Println(addr.String())
conn, err := net.ListenUDP("udp", addr)
if err != nil {
fmt.Println("Error listening:", err)
return
}
defer conn.Close()
fmt.Println("Server started at", addr)
for {
buffer := make([]byte, 1024)
n, clientAddr, err := conn.ReadFromUDP(buffer)
if err != nil {
continue
}
mu.Lock()
clients[clientAddr.String()] = clientAddr
mu.Unlock()
fmt.Println("Received from", clientAddr, ":", string(buffer[:n]))
// Notify other clients of new client
for addr := range clients {
if addr != clientAddr.String() {
// Send the new client's address to all other clients
conn.WriteToUDP([]byte(clientAddr.String()), clients[addr])
}
}
}
}
以上程序会将每一个UDP连接存储到Map中,当有新的连接进入的时候就会把map中的地址转发给它,客户端解析这个地址以后就给它发送UDP请求就可以了。
package main
import (
"fmt"
"net"
"strings"
"time"
)
func main() {
serverAddr, err := net.ResolveUDPAddr("udp6", "117.50.172.13:8172")
if err != nil {
fmt.Println("Error resolving server address:", err)
return
}
// 创建未连接的 UDP 套接字
conn, err := net.ListenUDP("udp6", nil)
if err != nil {
fmt.Println("Error listening:", err)
return
}
defer conn.Close()
// Send initial message to register with server
_, err = conn.WriteToUDP([]byte("REGISTER"), serverAddr)
if err != nil {
fmt.Println("Error sending register:", err)
return
}
go func() {
for {
_, err = conn.WriteToUDP([]byte("REGISTER"), serverAddr)
if err != nil {
fmt.Println("Error sending register:", err)
}
time.Sleep(1 * time.Second)
}
}()
// Listen for other clients' addresses
go func() {
buffer := make([]byte, 1024)
for {
n, addr, err := conn.ReadFromUDP(buffer)
if err != nil {
fmt.Println("Error reading from UDP:", err)
continue
}
clientAddr := string(buffer[:n])
fmt.Println("Received address from server:", clientAddr)
if !strings.Contains(clientAddr, ".") {
// 返回响应消息
conn.WriteToUDP([]byte("RESPONSE"), addr)
fmt.Println("获取到了别的客户端发送的消息:", clientAddr, addr.String())
time.Sleep(1 * time.Second)
continue
}
// Extract the IP and port from the received address
targetAddr, err := net.ResolveUDPAddr("udp", clientAddr)
if err != nil {
fmt.Println("Error resolving address:", err)
continue
}
// Send a message to the other client
fmt.Println("Sending message to:", targetAddr.String())
if _, err := conn.WriteToUDP([]byte("Hello from client!"), targetAddr); err != nil {
fmt.Println("Error sending message:", err)
}
}
}()
// Keep the client running
for {
time.Sleep(5 * time.Second)
}
}
这样就可以打洞成功了,但是需要注意的是两个客户端都是对称 NAT的情况下,以上程序是不能运行的。
因为在对称 NAT 中,对于每个内部设备与外部设备之间的连接,会创建一个唯一的映射。也就是说,如果同一个内部设备与不同的外部设备建立连接,它将使用不同的外部 IP 地址和端口。
评论列表
0/1000
共 0 评论