0.前言

一个项目,使用C#做UDP组播通信,发现这个过程中有丢包现象,做个测试看看哪种方式更好。

测试工具:visual studio 2022

语言:C#

框架:.netframework 4.5

网络通信库:udpClinet、socket

方式:本机收发

1.数据发送端

数据发送端用C#编写的winfrom程序,使用的是udpClient,参考代码如下:

   
   string msg="101010EF62";//测试发送的数据
   string[] str;
   try
   {
       string ip = "224.0.0.85";  //组播地址
       int port = 28889; //组播端口
       System.Net.Sockets.UdpClient udpClient = new System.Net.Sockets.UdpClient();
       str = Enumerable.Range(0, msg.Length / 2).Select(i => msg.Substring(i * 2, 2)).ToArray(); //分割成两个一组
       byte[] res = new byte[str.Length];
       for (int i = 0; i < str.Length; i++)
       {
           res[i] = Convert.ToByte(str[i], 16); //信息序号
       }
       udpClient.Send(res, res.Length, new IPEndPoint(IPAddress.Parse(ip), port));
   }
   catch (Exception ex)
   {
       Console.WriteLine(ex);
   }

注意,发送方式为没有任何间隔(一般做网络通信时要求有一定的间隔,来减少丢包率,此处为了测试性能,发送过程中没有任何间隔)

2.接收端-UdpClient方式

接收端为C#控制台程序(.netframework4.5),使用UdpClient默认方式如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace UDP_test
{
    internal class Program
    {
        private static UdpClient udpClient;  //UDP客户端
        static void Main(string[] args)
        {
            udpClient = new UdpClient(28889);
            udpClient.JoinMulticastGroup(IPAddress.Parse("224.0.0.85"));    // 加入组播组
            int sum = 0;//计数器
            Console.WriteLine("开始接收数据");
            while (true)
            {
                // 接收数据
                IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 28889);
                byte[] receiveData = udpClient.Receive(ref remoteEndPoint);
                string data = BitConverter.ToString(receiveData);
                //Console.WriteLine(data);   
                sum = sum + 1;
                Console.WriteLine(DateTime.Now.ToString("mm:ss fff") + "  第" + sum);
            }
        }
    }
}

发送211940条(每条按照218B计算,大概是211940*218/1024/1024=44MB),测试5次

发送持续时间 发送最后时间接收最后时间接收条数
206s47:14 10547:14 107204123
176s57:18 76257:18 762204026
151s02:20 50602:20 506199627
162s07:23 60507:23 606203649
174s14:20 01914:20 019202774

我们把缓冲区加大,代码参考如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace UDP_test
{
    internal class Program
    {
        private static UdpClient udpClient;  //UDP客户端
        static void Main(string[] args)
        {
            udpClient = new UdpClient(28889);
            udpClient.JoinMulticastGroup(IPAddress.Parse("224.0.0.85"));    // 加入组播组
            int sum = 0;//计数器
            Console.WriteLine("开始接收数据");
            udpClient.Client.ReceiveBufferSize = 8*1024*1024; //设置缓冲区
            while (true)
            {
                // 接收数据
                IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 28889);
                byte[] receiveData  = udpClient.Receive(ref remoteEndPoint);
                string data = BitConverter.ToString(receiveData);
                //Console.WriteLine(data);   
                sum = sum + 1;
                Console.WriteLine(DateTime.Now.ToString("mm:ss fff") + "  第" + sum);
            }
        }
    }
}

结果如下:

发送持续时间 发送最后时间接收最后时间接收条数
158s44:05 53144:05 531211940
164s50:14 85050:14 850211940
154s54:18 72654:18 728211940
153s59:04 86159:04 867211940
160s04:58 54304:58 543211940

结果发现并不丢包。

3.接收端—socket方式

参考代码如下:

   //接收数据缓冲池
   private static readonly ArrayPool<byte> BufferPool = ArrayPool<byte>.Shared;
 
   private void ReceiveData()
   {
    
       // 组播地址和端口
       IPAddress multicastAddress = IPAddress.Parse("224.0.0.85");
       int port = 28889;
       // 创建 UDP Socket
       Socket receiverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
       // 绑定到本地端口
       IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, port);
       receiverSocket.Bind(localEndPoint);
       // 加入组播组
       receiverSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(multicastAddress, IPAddress.Any));
       // 接收数据
       while (true)
       {
          byte[] buffer = BufferPool.Rent(1024);
           EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
           int length = receiverSocket.ReceiveFrom(buffer, ref remoteEndPoint);
           string message = BitConverter.ToString(buffer, 0, length);
           Console.WriteLine(message );
       }
   }

经过测试,发现使用socket+缓冲池的情况下,数据接收能力更好,丢包率更低