Go语言笔记:UDP基础使用与广播

Posted Naisu Xu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go语言笔记: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") // 转换地址,作为服务器使用时需要监听本机的一个端口
	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的使用比较简单,这里没有提及的是组播和任意播功能,因为我不太用这两种所以这里也就不介绍了。

以上是关于Go语言笔记:UDP基础使用与广播的主要内容,如果未能解决你的问题,请参考以下文章

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

GO语言UDP小笔记

GO1.6语言学习笔记1-基础篇

Node.js笔记:UDP基础使用

Node.js笔记:UDP基础使用

Go语言备忘录:反射的原理与使用详解