Golang笔记:UDP基础使用与广播

目的

UDP是比较基础常用的网络通讯方式,这篇文章将介绍Go语言中UDP基础使用的一些内容。

本文中使用 Packet Sender 工具进行测试,其官网地址如下:
https://packetsender.com/

基础说明

UDP是一种面向无连接的通讯,抛开业务逻辑来说UDP使用上不需要像TCP那样先建立连接才能使用,收就是收、发就是发,干净利落。

很多语言中UDP使用一般逻辑如下:

  1. 建立UDP类型Socket对象,用于数据收发;
  2. 发送时只需要指定 对方地址与端口号 就可以发送数据;
  3. 接收的话只需要监听 自身某个端口号
  4. 如果要实现双向通讯可以使用同一个Socket对象进行监听与发送;
  5. 很多时候都有一对一通讯的需求,所以标准库中都提供了一些面向连接的方法。但其实这只是在应用层面上的处理而已,实际上底层还是收就是收发就是发;

这里特别想要吐槽的一点是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 方法分别向各个广播地址发送消息。

总结

UDP的使用比较简单,这里没有提及的是组播和任意播功能,因为我不太用这两种所以这里也就不介绍了。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Naisu Xu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值