golang p2p udp server
http://www.dotcoo.com/golang-p2p-udp
package main
import (
"net"
"encoding/binary"
"encoding/hex"
"log"
"time"
)
const (
// login client -> server
CMD_LOGIN byte = byte(iota)
CMD_LOGIN_RES
// user list server -> client
CMD_LIST
CMD_LIST_RES
// ping server -> client
CMD_PING
CMD_PONG
// cone client -> client
CMD_CONE
CMD_CONE_RES
// message client -> client
CMD_MEG
CMD_MSG_RES
)
var userlist []*net.UDPAddr
var serverAddr *net.UDPAddr
var socket *net.UDPConn
func main() {
var err error
// 设置log参数
log.SetFlags(log.Lshortfile)
// 用户集合
userlist = make([]*net.UDPAddr, 0, 10)
// 服务器地址
serverAddr, err = net.ResolveUDPAddr("udp4", ":8080")
log.Println(err)
// 创建监听
socket, err = net.ListenUDP("udp4", serverAddr)
if err != nil {
log.Println("监听失败! ", err)
return
}
defer socket.Close()
// ping
// go ping()
// 处理
for {
// 处理数据报文
data := make([]byte, 4096)
read, addr, err := socket.ReadFromUDP(data)
if err != nil {
log.Println("读取数据失败!", err)
continue
}
log.Printf("UDP: %d, %s, %s\n", read, addr, hex.EncodeToString(data[:read]))
switch data[0] {
case CMD_LOGIN:
// 新上线用户的信息
touser_list_data := make([]byte, 0, 15)
touser_list_data = append(touser_list_data, CMD_LIST_RES, 0, 0, 0, 0, 0, 0)
// touser_list_data = append(touser_list_data, addr.IP...)
copy(touser_list_data[1:5], addr.IP)
binary.LittleEndian.PutUint16(touser_list_data[5:], uint16(addr.Port))
log.Println("touser_list_data:", hex.EncodeToString(touser_list_data))
// 构建在线的用户信息列表
user_list_data := make([]byte, 0, 100)
user_list_data = append(user_list_data, CMD_LIST_RES)
// 通知所有在线用户,有新用户上线
for _, touser := range userlist {
// 添加在线用户信息到列表
// user_list_data = append(user_list_data, touser.IP...)
user_list_data = append(user_list_data, 0, 0, 0, 0, 0, 0)
copy(user_list_data[len(user_list_data)-6:], touser.IP)
binary.LittleEndian.PutUint16(user_list_data[len(user_list_data)-2:], uint16(touser.Port))
// 给在线用户发送数据
socket.WriteToUDP(touser_list_data, touser)
}
// 给新上限用户发送在线用户的列表
log.Println("user_list_data:", hex.EncodeToString(user_list_data))
socket.WriteToUDP(user_list_data, addr)
// 将新用户存储
userlist = append(userlist, addr)
case CMD_LOGIN_RES:
case CMD_LIST:
case CMD_LIST_RES:
case CMD_PING:
case CMD_PONG:
log.Println("CMD_PONG udp: ", addr)
case CMD_CONE:
case CMD_CONE_RES:
case CMD_MEG:
case CMD_MSG_RES:
default:
log.Println("default udp: ", addr)
}
}
}
func ping() {
ping_data := make([]byte, 0, 15)
ping_data = append(ping_data, CMD_PING, 0)
for {
for _, touser := range userlist {
socket.WriteToUDP(ping_data, touser)
}
time.Sleep(5 * time.Second)
}
}
golang p2p udp client
package main
import (
"fmt"
"net"
"log"
"encoding/binary"
"encoding/hex"
"strings"
"bufio"
"os"
)
const (
// login client -> server
CMD_LOGIN byte = byte(iota)
CMD_LOGIN_RES
// user list server -> client
CMD_LIST
CMD_LIST_RES
// ping server -> client
CMD_PING
CMD_PONG
// cone client -> client
CMD_CONE
CMD_CONE_RES
// message client -> client
CMD_MEG
CMD_MSG_RES
)
var userlist []*net.UDPAddr
var serverAddr *net.UDPAddr
var listenAddr *net.UDPAddr
var socket *net.UDPConn
func main() {
var err error
// 设置log参数
log.SetFlags(log.Lshortfile)
// 用户集合
userlist = make([]*net.UDPAddr, 0, 10)
// 服务器地址
serverAddr, err = net.ResolveUDPAddr("udp4", "123.123.123.123:8080")
log.Println("serverAddr", err)
port := 8000
PORT:
// 本地地址
listenAddr, err = net.ResolveUDPAddr("udp4", fmt.Sprintf(":%d", port))
if err != nil {
log.Println(err)
}
// 创建连接
socket, err = net.ListenUDP("udp4", listenAddr)
if err != nil {
log.Println("连接失败!", err)
port++
goto PORT
return
}
defer socket.Close()
// 上线
login_data := make([]byte, 0, 10)
login_data = append(login_data, CMD_LOGIN)
login_data = append(login_data, []byte("nickname")...)
// 发送上线数据
_, err = socket.WriteToUDP(login_data, serverAddr)
if err != nil {
log.Println("发送数据失败!", err)
return
}
// 读取消息
go readMsg()
// 用户交互
readCmd()
}
// 用户交互
func readCmd() {
for {
fmt.Printf("p2p > ")
scanner := bufio.NewScanner(os.Stdin)
if !scanner.Scan() {
continue
}
var line = scanner.Text()
if err := scanner.Err(); err != nil {
log.Println("read error: ", err)
continue
}
switch {
case strings.HasPrefix(line, "help"):
fmt.Println(" list: show all user list\n send: send message\n\tsend ")
case strings.HasPrefix(line, "list"):
fmt.Println("user list:")
for id, user := range userlist {
fmt.Println(id+1, user.IP, user.Port)
}
case strings.HasPrefix(line, "send"):
id := 0;
content := ""
fmt.Sscanf(line, "send %d %s", &id, &content)
if id <= 0 || id > len(userlist) {
fmt.Printf("error: id %d not fund\n", id)
continue
}
log.Printf("send message: %s %d, %s", userlist[id-1], id, content)
sendData := make([]byte, 0, 100)
sendData = append(sendData, CMD_MEG)
sendData = append(sendData, []byte(content)...)
n, err := socket.WriteToUDP(sendData, userlist[id-1])
log.Println(n, err)
default:
fmt.Printf("command error: %s\nuse the 'help' command to get help\n", line)
}
}
}
func readMsg() {
for {
// 接收数据
data := make([]byte, 1024)
read, addr, err := socket.ReadFromUDP(data)
if err != nil {
log.Println("读取数据失败!", err)
continue
}
log.Printf("UDP: %d, %s, %s\n", read, addr, hex.EncodeToString(data[:read]))
switch data[0] {
case CMD_LOGIN_RES:
case CMD_LIST_RES:
for i := 1; i < read; i+=6 {
addrData := data[i:]
touser := &net.UDPAddr{
IP: net.IP(addrData[:4]),
Port: int(binary.LittleEndian.Uint16(addrData[4:])),
}
coneData := make([]byte, 0, 10)
coneData = append(coneData, CMD_CONE)
coneData = append(coneData, []byte("nickname")...)
socket.WriteToUDP(coneData, touser)
log.Println("cone: ", touser, coneData)
userlist = append(userlist, touser)
}
case CMD_PING:
log.Printf("CMD_PING\n")
pong_data := make([]byte, 0, 15)
pong_data = append(pong_data, CMD_PONG, 1)
n, err := socket.WriteTo(pong_data, addr)
log.Println("CMD_PING: ", n, err)
case CMD_PONG:
case CMD_CONE:
coneResData := make([]byte, 0, 10)
coneResData = append(coneResData, CMD_CONE_RES)
coneResData = append(coneResData, []byte("nickname")...)
socket.WriteToUDP(coneResData, addr)
case CMD_CONE_RES:
log.Println("CMD_CONE_RES:", addr)
case CMD_MEG:
fmt.Println(string(data[1:read]))
case CMD_MSG_RES:
default:
log.Println("default UDP: ", data[0])
}
}
}