Skip to content

Instantly share code, notes, and snippets.

@reterVision
Created July 12, 2014 07:50
Show Gist options
  • Save reterVision/33a72d70194d4a3c272e to your computer and use it in GitHub Desktop.
Save reterVision/33a72d70194d4a3c272e to your computer and use it in GitHub Desktop.
A dummy UDP hole punching sample in Go
package main
import (
"encoding/json"
"fmt"
"log"
"net"
"os"
"time"
)
type ChatRequest struct {
Action string
Username string
Message string
}
func main() {
if len(os.Args) < 5 {
log.Fatal("Usage: ", os.Args[0], " port serverAddr username peername")
}
port := fmt.Sprintf(":%s", os.Args[1])
serverAddr := os.Args[2]
username := os.Args[3]
peer := os.Args[4]
buf := make([]byte, 2048)
// Prepare to register user to server.
saddr, err := net.ResolveUDPAddr("udp4", serverAddr)
if err != nil {
log.Print("Resolve server address failed.")
log.Fatal(err)
}
// Prepare for local listening.
addr, err := net.ResolveUDPAddr("udp4", port)
if err != nil {
log.Print("Resolve local address failed.")
log.Fatal(err)
}
conn, err := net.ListenUDP("udp", addr)
if err != nil {
log.Print("Listen UDP failed.")
log.Fatal(err)
}
// Send registration information to server.
initChatRequest := ChatRequest{
"New",
username,
"",
}
jsonRequest, err := json.Marshal(initChatRequest)
if err != nil {
log.Print("Marshal Register information failed.")
log.Fatal(err)
}
_, err = conn.WriteToUDP(jsonRequest, saddr)
if err != nil {
log.Fatal(err)
}
log.Print("Waiting for server response...")
_, _, err = conn.ReadFromUDP(buf)
if err != nil {
log.Print("Register to server failed.")
log.Fatal(err)
}
// Send connect request to server
connectChatRequest := ChatRequest{
"Get",
username,
peer,
}
jsonRequest, err = json.Marshal(connectChatRequest)
if err != nil {
log.Print("Marshal connection information failed.")
log.Fatal(err)
}
var serverResponse ChatRequest
for i := 0; i < 3; i++ {
conn.WriteToUDP(jsonRequest, saddr)
n, _, err := conn.ReadFromUDP(buf)
if err != nil {
log.Print("Get peer address from server failed.")
log.Fatal(err)
}
err = json.Unmarshal(buf[:n], &serverResponse)
if err != nil {
log.Print("Unmarshal server response failed.")
log.Fatal(err)
}
if serverResponse.Message != "" {
break
}
time.Sleep(10 * time.Second)
}
if serverResponse.Message == "" {
log.Fatal("Cannot get peer's address")
}
log.Print("Peer address: ", serverResponse.Message)
peerAddr, err := net.ResolveUDPAddr("udp4", serverResponse.Message)
if err != nil {
log.Print("Resolve peer address failed.")
log.Fatal(err)
}
// Start chatting.
go listen(conn)
for {
fmt.Print("Input message: ")
message := make([]byte, 2048)
fmt.Scanln(&message)
messageRequest := ChatRequest{
"Chat",
username,
string(message),
}
jsonRequest, err = json.Marshal(messageRequest)
if err != nil {
log.Print("Error: ", err)
continue
}
conn.WriteToUDP(jsonRequest, peerAddr)
}
}
func listen(conn *net.UDPConn) {
for {
buf := make([]byte, 2048)
n, _, err := conn.ReadFromUDP(buf)
if err != nil {
log.Print(err)
continue
}
// log.Print("Message from ", addr.IP)
var message ChatRequest
err = json.Unmarshal(buf[:n], &message)
if err != nil {
log.Print(err)
continue
}
fmt.Println(message.Username, ":", message.Message)
}
}
package main
import (
"encoding/json"
"fmt"
"log"
"net"
)
var userIP map[string]string
type ChatRequest struct {
Action string
Username string
Message string
}
func main() {
userIP = map[string]string{}
service := ":9999"
udpAddr, err := net.ResolveUDPAddr("udp4", service)
if err != nil {
log.Fatal(err)
}
conn, err := net.ListenUDP("udp", udpAddr)
if err != nil {
log.Fatal(err)
}
for {
handleClient(conn)
}
}
/*
Action:
New -- Add a new user
Get -- Get a user IP address
Username:
New -- New user's name
Get -- The requested user name
*/
func handleClient(conn *net.UDPConn) {
var buf [2048]byte
n, addr, err := conn.ReadFromUDP(buf[0:])
if err != nil {
return
}
var chatRequest ChatRequest
err = json.Unmarshal(buf[:n], &chatRequest)
if err != nil {
log.Print(err)
return
}
switch chatRequest.Action {
case "New":
remoteAddr := fmt.Sprintf("%s:%d", addr.IP, addr.Port)
fmt.Println(remoteAddr, "connecting")
userIP[chatRequest.Username] = remoteAddr
// Send message back
messageRequest := ChatRequest{
"Chat",
chatRequest.Username,
remoteAddr,
}
jsonRequest, err := json.Marshal(&messageRequest)
if err != nil {
log.Print(err)
break
}
conn.WriteToUDP(jsonRequest, addr)
case "Get":
// Send message back
peerAddr := ""
if _, ok := userIP[chatRequest.Message]; ok {
peerAddr = userIP[chatRequest.Message]
}
messageRequest := ChatRequest{
"Chat",
chatRequest.Username,
peerAddr,
}
jsonRequest, err := json.Marshal(&messageRequest)
if err != nil {
log.Print(err)
break
}
_, err = conn.WriteToUDP(jsonRequest, addr)
if err != nil {
log.Print(err)
}
}
fmt.Println("User table:", userIP)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment