UDP是无连接的,也就是说理论上不区分客户端和服务器。 服务器可以在一个端口上接收不同的客户端。
客户端可以先绑定服务器地址到socket上,也可以不绑定,发送和接收数据都指定服务器地址。
下面是测试代码:
package main
import (
"encoding/json"
"fmt"
"net"
"testing"
"time"
)
const SvrAddr = "127.0.0.1:1111"
const CliAddr = "127.0.0.1:2222"
func ListenUDP(addrStr string) (*net.UDPAddr, *net.UDPConn, error) {
addr, err := net.ResolveUDPAddr("udp", addrStr)
if err != nil {
fmt.Println("resolve udp addr failed:", err)
return nil, nil, err
}
conn, err := net.ListenUDP("udp", addr)
if err != nil {
fmt.Println("listen udp svr failed:", err)
return nil, nil, err
}
return addr, conn, nil
}
type Msg struct {
Cmd string `json:"cmd"`
Seq int `json:"seq:`
}
func CliWriteTo(n int, cliConn *net.UDPConn, svrAddr *net.UDPAddr) {
for i := 0; i < n; i++ {
msg := Msg{
Cmd: "echo",
Seq: i,
}
data, err := json.Marshal(msg)
if err != nil {
fmt.Printf("send %d marshal failed:%v\n", i, err)
break
}
//这里write, cliConn未绑定
sendBytes, err := cliConn.WriteToUDP(data, svrAddr)
if err != nil {
fmt.Printf("send %d failed:%v\n", i, err)
break
}
if sendBytes != len(data) {
fmt.Printf("send failed, not equal send %v, but real %v\n", sendBytes, len(data))
}
var buf [128]byte
recvBytes, _, err := cliConn.ReadFromUDP(buf[:])
if err != nil {
fmt.Printf("recv failed:%v\n", err)
return
}
fmt.Println("CliRecv:", string(buf[:recvBytes]))
time.Sleep(time.Second)
}
}
func CliWrite(n int, cliConn *net.UDPConn, svrAddr *net.UDPAddr) {
for i := 0; i < n; i++ {
msg := Msg{
Cmd: "echo",
Seq: i,
}
data, err := json.Marshal(msg)
if err != nil {
fmt.Printf("send %d marshal failed:%v\n", i, err)
break
}
//这里write, cliConn已经绑定
sendBytes, err := cliConn.Write(data)
if err != nil {
fmt.Printf("send %d failed:%v\n", i, err)
break
}
if sendBytes != len(data) {
fmt.Printf("send failed, not equal send %v, but real %v\n", sendBytes, len(data))
}
var buf [128]byte
recvBytes, _, err := cliConn.ReadFromUDP(buf[:])
if err != nil {
fmt.Printf("recv failed:%v\n", err)
return
}
fmt.Println("CliRecv:", string(buf[:recvBytes]))
time.Sleep(time.Second)
}
}
func Server(svr *net.UDPConn) {
ch := make(chan struct{}, 10)
for {
ch <- struct{}{}
go func() {
defer func() {
<-ch
}()
var buf [128]byte
recvBytes, cli, err := svr.ReadFromUDP(buf[:])
if err != nil {
fmt.Printf("recv failed:%v\n", err)
return
}
//fmt.Println("SvrRecv:", string(buf[:recvBytes]))
sendBytes, err := svr.WriteToUDP(buf[:recvBytes], cli)
if err != nil {
fmt.Printf("send back %d failed\n", err)
return
}
if sendBytes != recvBytes {
fmt.Printf("send back failed, not equal recv %v, send %v\n", sendBytes, recvBytes)
}
}()
}
}
//UDP不区分客户端和服务器
//客户端和服务器可以同时Listen
//这个测试就是同时listen
func TestUDPSpeed(t *testing.T) {
svrAddr, svrConn, err := ListenUDP(SvrAddr)
if err != nil {
t.Fatal(err)
}
cliAddr, cliConn, err := ListenUDP(CliAddr)
if err != nil {
t.Fatal(err)
}
fmt.Println(svrAddr, svrConn, cliAddr, cliConn)
go Server(svrConn)
CliWriteTo(10, cliConn, svrAddr)
}
//UDP dial,绑定远端端口
func TestUDPSpeedDial(t *testing.T) {
svrAddr, svrConn, err := ListenUDP(SvrAddr)
if err != nil {
t.Fatal(err)
}
cliConn, err := net.DialUDP("udp", nil, svrAddr)
if err != nil {
t.Fatal(err)
}
fmt.Println(svrAddr, svrConn, nil, cliConn)
go Server(svrConn)
CliWrite(10, cliConn, svrAddr)
}
go test -v -run=Speed udp_test.go
go test -v -run=Dial udp_test.go