目的
UDP是比较基础常用的网络通讯方式,这篇文章将介绍Go语言中UDP基础使用的一些内容。
本文中使用 Packet Sender 工具进行测试,其官网地址如下:
https://packetsender.com/
基础说明
UDP是一种面向无连接的通讯,抛开业务逻辑来说UDP使用上不需要像TCP那样先建立连接才能使用,收就是收、发就是发,干净利落。
很多语言中UDP使用一般逻辑如下:
- 建立UDP类型Socket对象,用于数据收发;
- 发送时只需要指定 对方地址与端口号 就可以发送数据;
- 接收的话只需要监听 自身某个端口号 ;
- 如果要实现双向通讯可以使用同一个Socket对象进行监听与发送;
- 很多时候都有一对一通讯的需求,所以标准库中都提供了一些面向连接的方法。但其实这只是在应用层面上的处理而已,实际上底层还是收就是收发就是发;
这里特别想要吐槽的一点是Go标准库使用UDP收发数据并不是给的Socket对象,而都是需要通过 UDPConn
这个对象,这个思路就是上面的第5条思路了。我个人使用UDP单发送时比较喜欢上面第2条的方式,就是单纯的发送。在Go中找了半天没有找到这种可以直接发送的方式,感觉挺变扭的。
本文中用到的一些函数与方法如下:
// 从字符串获得IP地址
func ResolveUDPAddr(network, address string) (*UDPAddr, error)
// 建立UDP(预)连接
func DialUDP(network string, laddr, raddr *UDPAddr) (*UDPConn, error)
// 启动UDP监听
func ListenUDP(network string, laddr *UDPAddr) (*UDPConn, error)
// 建立连接的情况下发送数据
func (c *UDPConn) Write(b []byte) (int, error)
// 建立连接的情况下读取数据
func (c *UDPConn) Read(b []byte) (int, error)
// 获取远程地址
func (c *UDPConn) RemoteAddr() Addr
// 接收数据并获得远程地址
func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error)
// 向指定地址发送数据
func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error)
作为服务器使用
下面是个作为服务器使用的简单例子,功能是监听指定端口号,收到数据时输出到控制台,然后向远程端应答消息:
package main
import (
"fmt"
"net"
"os"
)
func main() {
udpAddr, err := net.ResolveUDPAddr("udp4", ":22333") // 转换地址,作为服务器使用时需要监听本机的一个端口
// 端口号写 0 可以由系统随机分配可用的端口号
checkError(err)
conn, err := net.ListenUDP("udp", udpAddr) // 启动UDP监听本机端口
checkError(err)
for {
var buf [128]byte
len, addr, err := conn.ReadFromUDP(buf[:]) // 读取数据,返回值依次为读取数据长度、远端地址、错误信息 // 读取操作会阻塞直至有数据可读取
checkError(err)
fmt.Println(string(buf[:len])) // 向终端打印收到的消息
_, err = conn.WriteToUDP([]byte("233~~~"), addr) // 写数据,返回值依次为写入数据长度、错误信息 // WriteToUDP()并非只能用于应答的,只要有个远程地址可以随时发消息
checkError(err)
}
}
func checkError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error %s", err.Error())
os.Exit(1)
}
}
作为客户端使用
作为客户端使用也不复杂,下面是个简单的示例:
package main
import (
"fmt"
"net"
"os"
"time"
)
func main() {
udpAddr, err := net.ResolveUDPAddr("udp4", "192.168.31.189:53771") // 转换地址,作为客户端使用要向远程发送消息,这里用远程地址与端口号
checkError(err)
conn, err := net.DialUDP("udp", nil, udpAddr) // 建立连接,第二个参数为nil时通过默认本地地址(猜测可能是第一个可用的地址,未进行测试)发送且端口号自动分配,第三个参数为远程端地址与端口号
checkError(err)
go receive(conn) // 使用DialUDP建立连接后也可以监听来自远程端的数据
for {
_, err = conn.Write([]byte("naisu233~~~")) // 向远程端发送消息
checkError(err)
time.Sleep(4 * time.Second) // 等待4s
}
}
func receive(conn *net.UDPConn) {
for {
var buf [128]byte
len, err := conn.Read(buf[0:]) // 读取数据 // 读取操作会阻塞直至有数据可读取
checkError(err)
fmt.Println(string(buf[0:len]))
}
}
func checkError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error %s", err.Error())
os.Exit(1)
}
}
广播
广播主要就是指发送时向内网中所有设备发送消息了,操作上最主要就是发送消息时地址使用 广播地址 ,广播地址计算方式可以参考下面文章:
《UDP IPv4广播地址计算(附Node.js示例代码)》
https://blog.csdn.net/Naisu_kun/article/details/127221349
下面是Go中广播地址获取代码:
// 返回广播地址列表
func GetBroadcastAddress() ([]string, error) {
broadcastAddress := []string{}
interfaces, err := net.Interfaces() // 获取所有网络接口
if err != nil {
return broadcastAddress, err
}
for _, face := range interfaces {
// 选择 已启用的、能广播的、非回环 的接口
if (face.Flags & (net.FlagUp | net.FlagBroadcast | net.FlagLoopback)) == (net.FlagBroadcast | net.FlagUp) {
addrs, err := face.Addrs() // 获取该接口下IP地址
if err != nil {
return broadcastAddress, err
}
for _, addr := range addrs {
if ipnet, ok := addr.(*net.IPNet); ok { // 转换成 IPNet { IP Mask } 形式
if ipnet.IP.To4() != nil { // 只取IPv4的
var fields net.IP // 用于存放广播地址字段(共4个字段)
for i := 0; i < 4; i++ {
fields = append(fields, (ipnet.IP.To4())[i]|(^ipnet.Mask[i])) // 计算广播地址各个字段
}
broadcastAddress = append(broadcastAddress, fields.String()) // 转换为字符串形式
}
}
}
}
}
return broadcastAddress, nil
}
因为工作原因我的电脑上有非常多的实体的或是虚拟的网卡,下面是我电脑上部分网络情况:
下面是广播地址获取演示:
需要注意的是如果设备上有多个网卡的话就可能有多个广播地址,要全局广播的话就要向每个广播地址分别发送消息。操作上来说可以用服务器的方式,拿到 UDPConn
对象后使用 WriteToUDP
方法分别向各个广播地址发送消息。文章来源:https://www.toymoban.com/news/detail-701860.html
总结
UDP的使用比较简单,这里没有提及的是组播和任意播功能,因为我不太用这两种所以这里也就不介绍了。文章来源地址https://www.toymoban.com/news/detail-701860.html
到了这里,关于Golang笔记:UDP基础使用与广播的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!