UDP步调设计

打印 上一主题 下一主题

主题 662|帖子 662|积分 1986

UDP协议概述

UDP,User Datagram Protocol,用户数据报协议,是一个简单的面向数据报(package-oriented)的传输层协议,规范为:RFC 768。
UDP提供数据的不可靠传递,它一旦把应用步调发给网络层的数据发送出去,就不保留数据备份。缺乏可靠性,缺乏拥塞控制(congestion control)。
根本示例

由于UDP是“无毗连”的,所以服务器端不需要创建监听套接字,只需要监听地址,等待客户端与之建立毗连,即可通信。
net包支持的典范UDP函数:
  1.  // 解析UDPAddr
  2.  func ResolveUDPAddr(network, address string) (*UDPAddr, error)
  3.  // 监听UDP地址
  4.  func ListenUDP(network string, laddr *UDPAddr) (*UDPConn, error)
  5.  // 连接UDP服务器
  6.  func DialUDP(network string, laddr, raddr *UDPAddr) (*UDPConn, error)
  7.  // UDP读
  8.  func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error)
  9.  // UDP写
  10.  func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error)
复制代码
编写示例,一次读写操作:
服务端流程:


  • 剖析UDP地址
  • 监听UDP
  • 读内容
  • 写内容
  1.  func UDPServerBasic() {
  2.      // 1.解析地址
  3.      laddr, err := net.ResolveUDPAddr("udp", ":9876")
  4.      if err != nil {
  5.          log.Fatalln(err)
  6.      }
  7.  ​
  8.      // 2.监听
  9.      udpConn, err := net.ListenUDP("udp", laddr)
  10.      if err != nil {
  11.          log.Fatalln(err)
  12.      }
  13.      log.Printf("%s server is listening on %s\n", "UDP", udpConn.LocalAddr().String())
  14.      defer udpConn.Close()
  15.  ​
  16.      // 3.读
  17.      buf := make([]byte, 1024)
  18.      rn, raddr, err := udpConn.ReadFromUDP(buf)
  19.      if err != nil {
  20.          log.Fatalln(err)
  21.      }
  22.      log.Printf("received %s from %s\n", string(buf[:rn]), raddr.String())
  23.  ​
  24.      // 4.写
  25.      data := []byte("received:" + string(buf[:rn]))
  26.      wn, err := udpConn.WriteToUDP(data, raddr)
  27.      if err != nil {
  28.          log.Fatalln(err)
  29.      }
  30.      log.Printf("send %s(%d) to %s\n", string(data), wn, raddr.String())
  31.  }
  32.  ​
复制代码
客户端流程:


  • 建立毗连
  • 写操作
  • 读操作
  1.  func UDPClientBasic() {
  2.      // 1.建立连接
  3.      raddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:9876")
  4.      if err != nil {
  5.          log.Fatalln(err)
  6.      }
  7.      udpConn, err := net.DialUDP("udp", nil, raddr)
  8.      if err != nil {
  9.          log.Fatalln(err)
  10.      }
  11.      log.Println(udpConn)
  12.  ​
  13.      // 2.写
  14.      data := []byte("Go UDP program")
  15.      wn, err := udpConn.Write(data) // WriteToUDP(data, raddr)
  16.      if err != nil {
  17.          log.Fatalln(err)
  18.      }
  19.      log.Printf("send %s(%d) to %s\n", string(data), wn, raddr.String())
  20.  ​
  21.      // 3.读
  22.      buf := make([]byte, 1024)
  23.      rn, raddr, err := udpConn.ReadFromUDP(buf)
  24.      if err != nil {
  25.          log.Fatalln(err)
  26.      }
  27.      log.Printf("received %s from %s\n", string(buf[:rn]), raddr.String())
  28.  }
复制代码
测试:
  1.  > go test -run UDPServerBasic
  2.  2023/05/25 17:26:34 UDP server is listening on [::]:9876
  3.  2023/05/25 17:29:22 received Go UDP program from 127.0.0.1:58657
  4.  2023/05/25 17:29:24 send received:Go UDP program(23) to 127.0.0.1:58657
复制代码
  1.  > go test -run UDPClientBasic
  2.  2023/05/25 17:29:22 &{{0xc000108f00}}
  3.  2023/05/25 17:29:22 send Go UDP program(14) to 127.0.0.1:9876
  4.  2023/05/25 17:29:24 received received:Go UDP program from 127.0.0.1:9876
复制代码
connected和unconnected的UDPConn

UDP的毗连分为:


  • 已毗连,connected, 利用方法 DialUDP建立的毗连,称为已毗连,pre-connected
  • 未毗连,unconnected,利用方法 ListenUDP 得到的毗连,称为未毗连,not connected
如果 *UDPConn是 connected,读写方法 Read和 Write。 如果 *UDPConn是 unconnected,读写方法 ReadFromUDP和 WriteToUDP
主要的差别在写操作上。读操作如果利用混乱,不会影响读操作本身,但一些参数细节上要留意:
示例:获取长途地址,conn.RemoteAddr()
unconnected,ListenUDP
  1.  func UDPServerConnect() {
  2.      // 1.解析地址
  3.      laddr, err := net.ResolveUDPAddr("udp", ":9876")
  4.      if err != nil {
  5.          log.Fatalln(err)
  6.      }
  7.  ​
  8.      // 2.监听
  9.      udpConn, err := net.ListenUDP("udp", laddr)
  10.      if err != nil {
  11.          log.Fatalln(err)
  12.      }
  13.      log.Printf("%s server is listening on %s\n", "UDP", udpConn.LocalAddr().String())
  14.      defer udpConn.Close()
  15.  ​
  16.      // 测试输出远程地址
  17.      log.Println(udpConn.RemoteAddr())
  18.  ​
  19.      // 3.读
  20.      buf := make([]byte, 1024)
  21.      rn, raddr, err := udpConn.ReadFromUDP(buf)
  22.      if err != nil {
  23.          log.Fatalln(err)
  24.      }
  25.      log.Printf("received %s from %s\n", string(buf[:rn]), raddr.String())
  26.  ​
  27.      // 测试输出远程地址
  28.      log.Println(udpConn.RemoteAddr())
  29.  ​
  30.      // 4.写
  31.      data := []byte("received:" + string(buf[:rn]))
  32.      wn, err := udpConn.WriteToUDP(data, raddr)
  33.      if err != nil {
  34.          log.Fatalln(err)
  35.      }
  36.      log.Printf("send %s(%d) to %s\n", string(data), wn, raddr.String())
  37.  ​
  38.      // 测试输出远程地址
  39.      log.Println(udpConn.RemoteAddr())
  40.  }
复制代码
测试:
  1.  > go test -run UDPServerConnect
  2.  2023/05/25 18:24:19 UDP server is listening on [::]:9876
  3.  2023/05/25 18:24:19 <nil>
  4.  2023/05/25 18:24:32 received Go UDP program from 127.0.0.1:63583
  5.  2023/05/25 18:24:35 <nil>
  6.  2023/05/25 18:24:35 send received:Go UDP program(23) to 127.0.0.1:63583
  7.  2023/05/25 18:24:35 <nil>
复制代码
connected,DialUDP
  1.  func UDPClientConnect() {
  2.      // 1.建立连接
  3.      raddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:9876")
  4.      if err != nil {
  5.          log.Fatalln(err)
  6.      }
  7.      udpConn, err := net.DialUDP("udp", nil, raddr)
  8.      if err != nil {
  9.          log.Fatalln(err)
  10.      }
  11.  ​
  12.      // 测试输出远程地址
  13.      log.Println(udpConn.RemoteAddr())
  14.  ​
  15.      // 2.写
  16.      data := []byte("Go UDP program")
  17.      wn, err := udpConn.Write(data) // WriteToUDP(data, raddr)
  18.      if err != nil {
  19.          log.Fatalln(err)
  20.      }
  21.      log.Printf("send %s(%d) to %s\n", string(data), wn, raddr.String())
  22.  ​
  23.      // 测试输出远程地址
  24.      log.Println(udpConn.RemoteAddr())
  25.  ​
  26.      // 3.读
  27.      buf := make([]byte, 1024)
  28.      rn, raddr, err := udpConn.ReadFromUDP(buf)
  29.      if err != nil {
  30.          log.Fatalln(err)
  31.      }
  32.      log.Printf("received %s from %s\n", string(buf[:rn]), raddr.String())
  33.  ​
  34.      // 测试输出远程地址
  35.      log.Println(udpConn.RemoteAddr())
  36.  }
复制代码
测试:
  1. > go test -run UDPClientConnect
  2. 2023/05/25 18:24:32 127.0.0.1:9876
  3. 2023/05/25 18:24:32 send Go UDP program(14) to 127.0.0.1:9876
  4. 2023/05/25 18:24:32 127.0.0.1:9876
  5. 2023/05/25 18:24:35 received received:Go UDP program from 127.0.0.1:9876
  6. 2023/05/25 18:24:38 127.0.0.1:9876
复制代码
示例:connected+WriteToUDP错误
  1. udpConn, err := net.DialUDP("udp", nil, raddr)
  2. wn, err := udpConn.WriteToUDP(data, raddr)
复制代码
测试:
  1. > go test -run UDPClientConnect
  2. 2023/05/25 18:27:41 127.0.0.1:9876
  3. 2023/05/25 18:27:41 write udp 127.0.0.1:52787->127.0.0.1:9876: use of WriteTo with pre-connected connection
复制代码
示例:unconnected+Write错误
  1. udpConn, err := net.ListenUDP("udp", laddr)
  2. wn, err := udpConn.Write(data)
复制代码
测试:
  1. write udp [::]:9876: wsasend: A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied.
复制代码
Read的利用只管遵循原则,但语法上可以混用,内部有兼容处理。
对等服务端和客户端

函数
  1. func ListenUDP(network string, laddr *UDPAddr) (*UDPConn, error)
复制代码
可以直接返回UDPConn,是unconnected状态。在编程时,我们的客户端和服务端都可以利用该函数建立UDP毗连。而不是一定要在客户端利用DialUDP函数。
如许创建的客户端和服务端时对等的关系。实用于例如广播类的网络通信项目中。
示例代码:
server:
  1. func UDPServerPeer() {
  2.         // 1.解析地址
  3.         laddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:9876")
  4.         if err != nil {
  5.                 log.Fatalln(err)
  6.         }
  7.         // 2.监听
  8.         udpConn, err := net.ListenUDP("udp", laddr)
  9.         if err != nil {
  10.                 log.Fatalln(err)
  11.         }
  12.         log.Printf("%s server is listening on %s\n", "UDP", udpConn.LocalAddr().String())
  13.         defer udpConn.Close()
  14.         // 远程地址
  15.         raddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:6789")
  16.         if err != nil {
  17.                 log.Fatalln(err)
  18.         }
  19.         // 3.读
  20.         buf := make([]byte, 1024)
  21.         rn, raddr, err := udpConn.ReadFromUDP(buf)
  22.         if err != nil {
  23.                 log.Fatalln(err)
  24.         }
  25.         log.Printf("received %s from %s\n", string(buf[:rn]), raddr.String())
  26.         // 4.写
  27.         data := []byte("received:" + string(buf[:rn]))
  28.         wn, err := udpConn.WriteToUDP(data, raddr)
  29.         if err != nil {
  30.                 log.Fatalln(err)
  31.         }
  32.         log.Printf("send %s(%d) to %s\n", string(data), wn, raddr.String())
  33. }
复制代码
client:
  1. func UDPClientPeer() {
  2.         // 1.解析地址
  3.         laddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:6789")
  4.         if err != nil {
  5.                 log.Fatalln(err)
  6.         }
  7.         // 2.监听
  8.         udpConn, err := net.ListenUDP("udp", laddr)
  9.         if err != nil {
  10.                 log.Fatalln(err)
  11.         }
  12.         log.Printf("%s server is listening on %s\n", "UDP", udpConn.LocalAddr().String())
  13.         defer udpConn.Close()
  14.         // 远程地址
  15.         raddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:9876")
  16.         if err != nil {
  17.                 log.Fatalln(err)
  18.         }
  19.         // 2.写
  20.         data := []byte("Go UDP program")
  21.         wn, err := udpConn.WriteToUDP(data, raddr)
  22.         if err != nil {
  23.                 log.Fatalln(err)
  24.         }
  25.         log.Printf("send %s(%d) to %s\n", string(data), wn, raddr.String())
  26.         // 3.读
  27.         buf := make([]byte, 1024)
  28.         rn, raddr, err := udpConn.ReadFromUDP(buf)
  29.         if err != nil {
  30.                 log.Fatalln(err)
  31.         }
  32.         log.Printf("received %s from %s\n", string(buf[:rn]), raddr.String())
  33. }
复制代码
测试:
  1. > go test -run UDPServerPeer
  2. 2023/05/25 19:08:34 UDP server is listening on 127.0.0.1:9876
  3. 2023/05/25 19:08:46 received Go UDP program from 127.0.0.1:6789
  4. 2023/05/25 19:08:46 send received:Go UDP program(23) to 127.0.0.1:6789
复制代码
  1. > go test -run UDPClientPeer
  2. 2023/05/25 19:08:46 UDP server is listening on 127.0.0.1:6789
  3. 2023/05/25 19:08:46 send Go UDP program(14) to 127.0.0.1:9876
  4. 2023/05/25 19:08:46 received received:Go UDP program from 127.0.0.1:9876
复制代码
多播编程

多播(Multicast)方式的数据传输是基于 UDP 完成的。与 UDP 服务器端/客户端的单播方式不同,区别是,单播数据传输以单一目标进行,而多播数据同时传递到参加(注册)特定组的大量主机。换言之,采用多播方式时,可以同时向多个主机传递数据。
多播的特点如下:


  • 多播发送端针对特定多播组
  • 发送端发送 1 次数据,但该组内的所有接收端都会接收数据
  • 多播组数可以在 IP 地址范围内任意增长
如图所示:


多播组是 D 类IP地址(224.0.0.0~239.255.255.255):


  • 224.0.0.0~224.0.0.255为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其它地址供路由协议利用;
  • 224.0.1.0~224.0.1.255是公用组播地址,Internetwork Control Block;
  • 224.0.2.0~238.255.255.255为用户可用的组播地址(临时组地址),全网范围内有用;
  • 239.0.0.0~239.255.255.255为本地管理组播地址,仅在特定的本地范围内有用
Go的尺度库net支持多播编程,主要的函数:
  1. func ListenMulticastUDP(network string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error)
复制代码
用于创建多播的UDP毗连。
示例:多播通信
接收端端
  1. // 多播接收端
  2. func UDPReceiverMulticast() {
  3.         // 1.多播监听地址
  4.         address := "224.1.1.2:6789"
  5.         gaddr, err := net.ResolveUDPAddr("udp", address)
  6.         if err != nil {
  7.                 log.Fatalln(err)
  8.         }
  9.         // 2.多播监听
  10.         udpConn, err := net.ListenMulticastUDP("udp", nil, gaddr)
  11.         if err != nil {
  12.                 log.Fatalln(err)
  13.         }
  14.         defer udpConn.Close()
  15.         log.Printf("%s server is listening on %s\n", "UDP", udpConn.LocalAddr().String())
  16.         // 3.接受数据
  17.         // 循环接收
  18.         buf := make([]byte, 1024)
  19.         for {
  20.                 rn, raddr, err := udpConn.ReadFromUDP(buf)
  21.                 if err != nil {
  22.                         log.Println(err)
  23.                 }
  24.                 log.Printf("received "%s" from %s\n", string(buf[:rn]), raddr.String())
  25.         }
  26. }
复制代码
发送端:
  1. // 多播的发送端
  2. func UDPSenderMulticast() {
  3.         // 1.建立UDP多播组连接
  4.         address := "224.1.1.2:6789"
  5.         raddr, err := net.ResolveUDPAddr("udp", address)
  6.         if err != nil {
  7.                 log.Fatalln(err)
  8.         }
  9.         udpConn, err := net.DialUDP("udp", nil, raddr)
  10.         if err != nil {
  11.                 log.Fatalln(err)
  12.         }
  13.         defer udpConn.Close()
  14.         // 2.发送内容
  15.         // 循环发送
  16.         for {
  17.                 data := fmt.Sprintf("[%s]: %s", time.Now().Format("03:04:05.000"), "hello!")
  18.                 wn, err := udpConn.Write([]byte(data))
  19.                 if err != nil {
  20.                         log.Println(err)
  21.                 }
  22.                 log.Printf("send "%s"(%d) to %s\n", data, wn, raddr.String())
  23.                 time.Sleep(time.Second)
  24.         }
  25. }
复制代码
测试:
启动发送端:
  1. # go test -run UDPSenderMulticast
  2. 2023/05/26 16:36:43 send "[04:36:43.976]: hello!"(22) to 224.1.1.2:6789
  3. 2023/05/26 16:36:44 send "[04:36:44.977]: hello!"(22) to 224.1.1.2:6789
  4. 2023/05/26 16:36:45 send "[04:36:45.979]: hello!"(22) to 224.1.1.2:6789
  5. 2023/05/26 16:36:46 send "[04:36:46.980]: hello!"(22) to 224.1.1.2:6789
复制代码
启动多个接收端,也可以在过程中继续启动:
  1. # go test -run UDPReceiverMulticast
  2. 2023/05/26 16:36:00 UDP server is listening on 0.0.0.0:6789
  3. 2023/05/26 16:36:00 received "[04:36:43.499]: hello!" from 192.168.50.130:56777
  4. 2023/05/26 16:36:01 received "[04:36:43.500]: hello!" from 192.168.50.130:56777
  5. 2023/05/26 16:36:02 received "[04:36:43.500]: hello!" from 192.168.50.130:56777
复制代码
  1. # go test -run UDPReceiverMulticast
  2. 2023/05/26 16:36:00 UDP server is listening on 0.0.0.0:6789
  3. 2023/05/26 16:36:00 received "[04:36:43.499]: hello!" from 192.168.50.130:56777
  4. 2023/05/26 16:36:01 received "[04:36:44.500]: hello!" from 192.168.50.130:56777
  5. 2023/05/26 16:36:02 received "[04:36:45.500]: hello!" from 192.168.50.130:56777
复制代码
附:Goland长途开发步调截图:



  • 建立ssh毗连
  • 打开项目






















广播编程

广播地址,Broadcast,指的是将消息发送到在同一广播网络上的每个主机。
例如对于网络:
  1. # ip a
  2. ens33: <BROADCAST,MULTICAST,UP,LOWER_UP>
  3. inet 192.168.50.130/24 brd 192.168.50.255
复制代码
来说,IP ADDR 就是 192.168.50.130/24, 广播地址就是 192.168.50.255
意味着,只要发送数据包的目标地址(接收地址)为192.168.50.255时,那么该数据会发送给该网段上的所有计算机。
如图:


编码实现:
编码时数据发的发送端,同样利用 ListenUDP 方法建立UDP毗连,调用WriteToUDP完成数据的发送。就是上面的对等服务端和客户端模式。
接收端:
  1. // 广播接收端
  2. func UDPReceiverBroadcast() {
  3.         // 1.广播监听地址
  4.         laddr, err := net.ResolveUDPAddr("udp", ":6789")
  5.         if err != nil {
  6.                 log.Fatalln(err)
  7.         }
  8.         // 2.广播监听
  9.         udpConn, err := net.ListenUDP("udp", laddr)
  10.         if err != nil {
  11.                 log.Fatalln(err)
  12.         }
  13.         defer udpConn.Close()
  14.         log.Printf("%s server is listening on %s\n", "UDP", udpConn.LocalAddr().String())
  15.         // 3.接收数据
  16.         // 4.处理数据
  17.         buf := make([]byte, 1024)
  18.         for {
  19.                 rn, raddr, err := udpConn.ReadFromUDP(buf)
  20.                 if err != nil {
  21.                         log.Println(err)
  22.                 }
  23.                 log.Printf("received "%s" from %s\n", string(buf[:rn]), raddr.String())
  24.         }
  25. }
复制代码
发送端:
  1. // 广播发送端
  2. func UDPSenderBroadcast() {
  3.         // 1.监听地址
  4.         // 2.建立连接
  5.         laddr, err := net.ResolveUDPAddr("udp", ":9876")
  6.         if err != nil {
  7.                 log.Fatalln(err)
  8.         }
  9.         udpConn, err := net.ListenUDP("udp", laddr)
  10.         if err != nil {
  11.                 log.Fatalln(err)
  12.         }
  13.         defer udpConn.Close()
  14.         log.Printf("%s server is listening on %s\n", "UDP", udpConn.LocalAddr().String())
  15.         // 3.发送数据
  16.         // 广播地址
  17.         rAddress := "192.168.50.255:6789"
  18.         raddr, err := net.ResolveUDPAddr("udp", rAddress)
  19.         if err != nil {
  20.                 log.Fatalln(err)
  21.         }
  22.         for {
  23.                 data := fmt.Sprintf("[%s]: %s", time.Now().Format("03:04:05.000"), "hello!")
  24.                 // 广播发送
  25.                 wn, err := udpConn.WriteToUDP([]byte(data), raddr)
  26.                 if err != nil {
  27.                         log.Println(err)
  28.                 }
  29.                 log.Printf("send "%s"(%d) to %s\n", data, wn, raddr.String())
  30.                 time.Sleep(time.Second)
  31.         }
  32. }
复制代码
测试:
接收端:
  1. # go test -run UDPReceiverBroadcast
  2. 2023/06/01 17:13:27 UDP server is listening on [::]:6789
  3. 2023/06/01 17:13:34 received "[05:13:34.707]: hello!" from 192.168.50.130:9876
  4. 2023/06/01 17:13:35 received "[05:13:35.709]: hello!" from 192.168.50.130:9876
复制代码
发送端:
  1. # go test -run UDPSenderBroadcast
  2. 2023/06/01 17:13:34 UDP server is listening on [::]:9876
  3. 2023/06/01 17:13:34 send "[05:13:34.707]: hello!"(22) to 192.168.50.255:6789
  4. 2023/06/01 17:13:35 send "[05:13:35.709]: hello!"(22) to 192.168.50.255:6789
复制代码
文件传输案例

案例阐明

UDP协议在传输数据时,由于不能包管稳定性传输,因此比较适合多媒体通信范畴,例如直播、视频、音频即时播放,即时通信等范畴。
本案例利用文件传输为例。
客户端设计:


  • 发送文件mp3(任意类型都ok)
  • 发送文件名
  • 发送文件内容
服务端设计:


  • 接收文件
  • 存储为客户端发送的名字
  • 接收文件内容
  • 写入到详细文件中
编码实现

客户端:
  1. // 文件传输(上传)
  2. func UDPFileClient() {
  3.         // 1.获取文件信息
  4.         filename := "./data/Beyond.mp3"
  5.         // 打开文件
  6.         file, err := os.Open(filename)
  7.         if err != nil {
  8.                 log.Fatalln(err)
  9.         }
  10.         // 关闭文件
  11.         defer file.Close()
  12.         // 获取文件信息
  13.         fileinfo, err := file.Stat()
  14.         if err != nil {
  15.                 log.Fatalln(err)
  16.         }
  17.         //fileinfo.Size(), fileinfo.Name()
  18.         log.Println("send file size:", fileinfo.Size())
  19.         // 2.连接服务器
  20.         raddress := "192.168.50.131:5678"
  21.         raddr, err := net.ResolveUDPAddr("udp", raddress)
  22.         if err != nil {
  23.                 log.Fatalln(err)
  24.         }
  25.         udpConn, err := net.DialUDP("udp", nil, raddr)
  26.         if err != nil {
  27.                 log.Fatalln(err)
  28.         }
  29.         defer udpConn.Close()
  30.         // 3.发送文件名
  31.         if _, err := udpConn.Write([]byte(fileinfo.Name())); err != nil {
  32.                 log.Fatalln(err)
  33.         }
  34.         // 4.服务端确认
  35.         buf := make([]byte, 4*1024)
  36.         rn, err := udpConn.Read(buf)
  37.         if err != nil {
  38.                 log.Fatalln(err)
  39.         }
  40.         // 判断是否为文件名正确接收响应
  41.         if "filename ok" != string(buf[:rn]) {
  42.                 log.Fatalln(errors.New("server not ready"))
  43.         }
  44.         // 5.发送文件内容
  45.         // 读取文件内容,利用连接发送到服务端
  46.         // file.Read()
  47.         i := 0
  48.         for {
  49.                 // 读取文件内容
  50.                 rn, err := file.Read(buf)
  51.                 if err != nil {
  52.                         // io.EOF 错误表示文件读取完毕
  53.                         if err == io.EOF {
  54.                                 break
  55.                         }
  56.                         log.Fatalln(err)
  57.                 }
  58.                 // 发送到服务端
  59.                 if _, err := udpConn.Write(buf[:rn]); err != nil {
  60.                         log.Fatalln(err)
  61.                 }
  62.                 i++
  63.         }
  64.         log.Println(i)
  65.         // 文件发送完成。
  66.         log.Println("file send complete.")
  67.         // 等待的测试
  68.         time.Sleep(2 * time.Second)
  69. }
复制代码
服务端:
  1. // UDP文件传输
  2. func UDPFileServer() {
  3.         // 1.建立UDP连接
  4.         laddress := ":5678"
  5.         laddr, err := net.ResolveUDPAddr("udp", laddress)
  6.         if err != nil {
  7.                 log.Fatalln(err)
  8.         }
  9.         udpConn, err := net.ListenUDP("udp", laddr)
  10.         if err != nil {
  11.                 log.Fatalln(err)
  12.         }
  13.         defer udpConn.Close()
  14.         log.Printf("%s server is listening on %s\n", "UDP", udpConn.LocalAddr().String())
  15.         // 2.接收文件名,并确认
  16.         buf := make([]byte, 4*1024)
  17.         rn, raddr, err := udpConn.ReadFromUDP(buf)
  18.         if err != nil {
  19.                 log.Fatalln(err)
  20.         }
  21.         filename := string(buf[:rn])
  22.         if _, err := udpConn.WriteToUDP([]byte("filename ok"), raddr); err != nil {
  23.                 log.Fatalln(err)
  24.         }
  25.         // 3.接收文件内容,并写入文件
  26.         // 打开文件(创建)
  27.         file, err := os.Create(filename)
  28.         if err != nil {
  29.                 log.Fatalln(err)
  30.         }
  31.         defer file.Close()
  32.         // 网络读取
  33.         i := 0
  34.         for {
  35.                 // 一次读取
  36.                 rn, _, err := udpConn.ReadFromUDP(buf)
  37.                 if err != nil {
  38.                         log.Fatalln(err)
  39.                 }
  40.                 // 写入文件
  41.                 if _, err := file.Write(buf[:rn]); err != nil {
  42.                         log.Fatalln(err)
  43.                 }
  44.                 i++
  45.                 log.Println("file write some content", i)
  46.         }
  47. }
复制代码
测试,将文件从win传输到linux(centos)中。
上传乐成,但文件内容未完备接收,留意这个UDP内容传输的特点(劣势)
  1. # ll
  2. total 16344
  3. -rw-r--r--. 1 root root 9954453 Jun  2 18:08 Beyond.mp3
  4. # ll
  5. total 16344
  6. -rw-r--r--. 1 root root 9757845 Jun  2 18:14 Beyond.mp3
复制代码
对比源文件巨细:
  1. > go test -run UDPFileClient
  2. 2023/06/02 18:14:54 send file size: 10409109
复制代码
小结



  • UDP,User Datagram Protocol,用户数据报协议,是一个简单的面向数据报(package-oriented)的传输层协议
  • 单播,点对点
  • 多播,组内,利用多播(组播)地址
  • 广播,网段内,利用广播地址
  • udp毗连

    • connected, net.DialUDP, Read, Write
    • unconnected, net.ListenUDP, ReadFromUDP, WriteToUDP

  • 场景:

    • 实时性要求高
    • 完备性要求不高


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

星球的眼睛

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表