Last active
September 3, 2019 07:31
-
-
Save guileen/b9cc825ed18eb8350539 to your computer and use it in GitHub Desktop.
Go P2P Demo
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 <id> <message>") | |
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]) | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// golang p2p udp server | |
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) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Fix: p2pclient 位于 NAT 之后, p2pserver 位于公网,测试不通过
https://gist.github.com/guileen/b9cc825ed18eb8350539#file-p2pclient-go-L56-L72 处的代码:
建议改为:
这样,client 是随机建立的端口。但分布于异地网络中的2个 p2pclient 都连接上 p2pserver 后,只要 p2pclient 为 Full cone NAT ,即可相互通信。