2.3 UDP 服务器和客户端开发
UDP 协议是非面向连接
在 UDP 种服务端开发和客户端开发都和 TCP 一样的。三个最主要的因素都是地址和协议还有链接
在 UDP 开发种没有监听
// 开启 UDP 链接
func net.ListenPacket(network string, address string) (net.PacketConn, error)
// 读取数据
func (net.PacketConn).ReadFrom(p []byte) (n int, addr net.Addr, err error)
2.3.1 服务器端不回复客户端消息范例
服务器端代码
package main
import (
"fmt"
"net"
)
func main() {
// 监听所有网卡的 8888 端口
addr := ":8888"
// 协议使用 udp
protocol := "udp"
// 启动 UDP 链接
packetConn, err := net.ListenPacket(protocol, addr)
if err != nil {
fmt.Println(err)
return
}
// 处理客户端
// 通过 for 循环不断开连接读取客户端发来的数据,每次读取 1024 字节
for {
ctx := make([]byte, 1024)
// 读取数据的时候肯定是需要知道从谁读取的,我们肯定需要告诉服务器端吧
n, addr, err := packetConn.ReadFrom(ctx)
if err != nil {
fmt.Println(err)
continue
}
// 输出 客户端 发送的数据
fmt.Printf("客户端[%s]发送数据:%s\n", addr, string(ctx[:n]))
}
// 关闭服务器端
packetConn.Close()
}
客户端代码
package main
import (
"fmt"
"net"
"time"
)
func main() {
addr := "127.0.0.1:8888"
protocol := "udp"
// UDP 客户端链接 server 端也是使用 net.Dial()
conn, err := net.Dial(protocol, addr)
if err != nil {
fmt.Println(err)
return
}
// 客户端往服务器端发送数据
n, err := conn.Write([]byte(time.Now().Format("2006-01-02 15:04:05")))
fmt.Println(n, err)
// 接收服务器端发送数据,因为 read 需要接收一个 []byte, 所以定义 cxt 的字节切皮变量
cxt := make([]byte, 1024)
// read 类似于一个寄存器,不接收到的数据寄存在 cxt 切片中,获取接收到的数据长度
n, err = conn.Read(cxt)
fmt.Printf("服务器端发送数据:%s\n", string(cxt[:n]))
// 关闭连接
conn.Close()
}
-
我们直接运行客户端
[17:47:26 root@go UDP]#go run client.go
21 <nil>
# 通过运行我们会发现即使没有启动 server 端,客户端也不会报错
# 因为 UDP 协议中客户端发送数据的时候并不会关心服务器端是否真的存在
-
接着我们这次先启动服务器端
-
先启动 server 端
-
在启动 client 端
-
但是这个时候服务器端并不能向客户端回复消息。所以还的优化 server 端代码
2.3.2 server 端向客户端回复消息范例
server 端需要向 客户端 回复消息要使用到 net.WriteTo()
方法,而且我们只需要修改 server 代码即可
[18:04:13 root@go UDP]#go doc net.writeTo
package net // import "net"
func (v *Buffers) WriteTo(w io.Writer) (n int64, err error)
func (c *IPConn) WriteTo(b []byte, addr Addr) (int, error)
WriteTo implements the PacketConn WriteTo method.
# 通过查看 go doc net.WriteTo() 方法是有两个参数一个是写入的字节切,一个是回复地址
func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error)
WriteTo implements the PacketConn WriteTo method.
func (c *UnixConn) WriteTo(b []byte, addr Addr) (int, error)
WriteTo implements the PacketConn WriteTo method.
server 端代码
package main
import (
"fmt"
"net"
)
func main() {
// 监听所有网卡的 8888 端口
addr := ":8888"
// 协议使用 udp
protocol := "udp"
// 启动 UDP 链接
packetConn, err := net.ListenPacket(protocol, addr)
if err != nil {
fmt.Println(err)
return
}
// 处理客户端
// 通过 for 循环读取数据,每次读取 1024 字节
for {
ctx := make([]byte, 1024)
// 读取数据的时候肯定是需要知道从谁读取的,我们肯定需要告诉服务器端吧
n, addr, err := packetConn.ReadFrom(ctx)
if err != nil {
fmt.Println(err)
continue
}
// 输出 客户端 发送的数据
fmt.Printf("客户端[%s]发送数据:%s\n", addr, string(ctx[:n]))
// 服务器端向客户端发送数据"我是服务器"
fmt.Println(packetConn.WriteTo([]byte(string("我是服务器")), addr))
}
// 关闭服务器端
packetConn.Close()
}
通过执行 server 端程序,而且 客户端未接收也不会报错,这个就是 UDP 和 TCP 协议之间的区别。
UDP 服务器端在发送数据的时候不会管客户端是否接收到
-
执行服务器端
-
执行客户端
-
客户端接收到了服务器端发送的数据