• 2019-10-30-C#-dotnet-core-局域网组播方法


    title author date CreateTime categories
    C# dotnet core 局域网组播方法
    lindexi
    2019-10-30 9:0:48 +0800
    2019-10-29 12:2:46 +0800
    dotnet C#

    我在微软官网找到了用 C# 做 UDP 组播的方法,我优化一些逻辑,保留核心代码,然后加了一点封装

    在使用之前需要注意的是组播可以用来做局域网传输,但是组播不是可靠的方案,随时可能因为路由器等发送失败或无法接收消息

    使用组播的方法是创建 Socket 通过 UDP 向组播地址发送数据或从组播地址接收数据

    可以作为组播的地址是 239.0.0.0~239.255.255.255 的范围,这个范围是局域网可用。但实际可用或不可用还需要靠实际的路由器

    首先创建一个 Socket 然后绑定到端口

            private IPAddress LocalIpAddress { set; get; } = IPAddress.Any;
    
            private Socket MulticastSocket { get; }
    
            private const int MulticastPort = 15003;
    
            private void TryBindSocket()
            {
                for (var i = MulticastPort; i < 65530; i++)
                {
                    try
                    {
                        EndPoint localEndPoint = new IPEndPoint(LocalIpAddress, i);
    
                        MulticastSocket.Bind(localEndPoint);
                        return;
                    }
                    catch (SocketException e)
                    {
                        Console.WriteLine(e);
                    }
                }
            }

    绑定的端口是用来接收的端口,所以绑定失败不会影响发送

    绑定完成需要加入组播网络

                    var multicastOption = new MulticastOption(MulticastAddress, IPAddress.Any);
    
                    MulticastSocket.SetSocketOption(SocketOptionLevel.IP,
                        SocketOptionName.AddMembership,
                        multicastOption);
    
            /// <summary>
            /// 组播地址
            /// <para/>
            /// 224.0.0.0~224.0.0.255为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其它地址供路由协议使用;
            /// <para/>
            /// 224.0.1.0~224.0.1.255是公用组播地址,可以用于Internet;
            /// <para/>
            /// 224.0.2.0~238.255.255.255为用户可用的组播地址(临时组地址),全网范围内有效;
            /// <para/>
            /// 239.0.0.0~239.255.255.255为本地管理组播地址,仅在特定的本地范围内有效。
            /// </summary>
            public IPAddress MulticastAddress { set; get; }

    需要注意,上面代码的 LocalIpAddress 写的是 Any 也就是只有在默认的网卡是和其他设备网段才能访问

    如果发现其他设备不能接收到信息,那么请修改 LocalIpAddress 为你设备的地址

    接收方法和接收其他相同

            private void ReceiveBroadcastMessages()
            {
                // 接收需要绑定 MulticastPort 端口
                var bytes = new byte[MaxByteLength];
                EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
    
                try
                {
                    while (true)
                    {
                        var length = MulticastSocket.ReceiveFrom(bytes, ref remoteEndPoint);
    
                        Console.WriteLine(Encoding.UTF8.GetString(bytes, 0, length));
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.ToString());
                }
            }

    接收和发送都是二进制

            /// <summary>
            /// 发送组播
            /// </summary>
            /// <param name="message"></param>
            public void SendBroadcastMessage(string message)
            {
                try
                {
                    var endPoint = new IPEndPoint(MulticastAddress, MulticastPort);
                    var byteList = Encoding.UTF8.GetBytes(message);
    
                    MulticastSocket.SendTo(byteList, endPoint);
                }
                catch (Exception e)
                {
                    Console.WriteLine("
    " + e);
                }
            }

    所有代码

        internal class PeerMulticastFinder : IDisposable
        {
            /// <inheritdoc />
            public PeerMulticastFinder()
            {
                MulticastSocket = new Socket(AddressFamily.InterNetwork,
                    SocketType.Dgram,
                    ProtocolType.Udp);
                MulticastAddress = IPAddress.Parse("230.138.100.2");
            }
    
            /// <summary>
            /// 寻找局域网设备
            /// </summary>
            public void FindPeer()
            {
                // 实际是反过来,让其他设备询问
    
                StartMulticast();
    
                var ipList = GetLocalIpList().ToList();
                var message = string.Join(';',ipList);
                SendBroadcastMessage(message);
                // 先发送再获取消息,这样就不会收到自己发送的消息
                ReceivedMessage += (s, e) => { Console.WriteLine($"找到 {e}"); };
            }
    
            /// <summary>
            /// 获取本地 IP 地址
            /// </summary>
            /// <returns></returns>
            private IEnumerable<IPAddress> GetLocalIpList()
            {
                var host = Dns.GetHostEntry(Dns.GetHostName());
                foreach (var ip in host.AddressList)
                {
                    if (ip.AddressFamily == AddressFamily.InterNetwork)
                    {
                        yield return ip;
                    }
                }
            }
    
            /// <summary>
            /// 组播地址
            /// <para/>
            /// 224.0.0.0~224.0.0.255为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其它地址供路由协议使用;
            /// <para/>
            /// 224.0.1.0~224.0.1.255是公用组播地址,可以用于Internet;
            /// <para/>
            /// 224.0.2.0~238.255.255.255为用户可用的组播地址(临时组地址),全网范围内有效;
            /// <para/>
            /// 239.0.0.0~239.255.255.255为本地管理组播地址,仅在特定的本地范围内有效。
            /// </summary>
            public IPAddress MulticastAddress { set; get; }
    
            private const int MulticastPort = 15003;
    
            /// <summary>
            /// 启动组播
            /// </summary>
            public void StartMulticast()
            {
                try
                {
                    // 如果首次绑定失败,那么将无法接收,但是可以发送
                    TryBindSocket();
    
                    // Define a MulticastOption object specifying the multicast group 
                    // address and the local IPAddress.
                    // The multicast group address is the same as the address used by the server.
                    // 有多个 IP 时,指定本机的 IP 地址,此时可以接收到具体的内容
                    var multicastOption = new MulticastOption(MulticastAddress, IPAddress.Parse("172.18.134.16"));
    
                    MulticastSocket.SetSocketOption(SocketOptionLevel.IP,
                        SocketOptionName.AddMembership,
                        multicastOption);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.ToString());
                }
    
                Task.Run(ReceiveBroadcastMessages);
            }
    
            /// <summary>
            /// 收到消息
            /// </summary>
            public event EventHandler<string> ReceivedMessage;
    
            private void ReceiveBroadcastMessages()
            {
                // 接收需要绑定 MulticastPort 端口
                var bytes = new byte[MaxByteLength];
                EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
    
                try
                {
                    while (!_disposedValue)
                    {
                        var length = MulticastSocket.ReceiveFrom(bytes, ref remoteEndPoint);
    
                        OnReceivedMessage(Encoding.UTF8.GetString(bytes, 0, length));
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.ToString());
                }
            }
    
            /// <summary>
            /// 发送组播
            /// </summary>
            /// <param name="message"></param>
            public void SendBroadcastMessage(string message)
            {
                try
                {
                    var endPoint = new IPEndPoint(MulticastAddress, MulticastPort);
                    var byteList = Encoding.UTF8.GetBytes(message);
    
                    if (byteList.Length > MaxByteLength)
                    {
                        throw new ArgumentException($"传入 message 转换为 byte 数组长度太长,不能超过{MaxByteLength}字节")
                        {
                            Data =
                            {
                                { "message", message },
                                { "byteList", byteList }
                            }
                        };
                    }
    
                    MulticastSocket.SendTo(byteList, endPoint);
                }
                catch (Exception e)
                {
                    Console.WriteLine("
    " + e);
                }
            }
    
            private IPAddress LocalIpAddress { set; get; } = IPAddress.Any;
    
            private Socket MulticastSocket { get; }
    
            private void TryBindSocket()
            {
                for (var i = MulticastPort; i < 65530; i++)
                {
                    try
                    {
                        EndPoint localEndPoint = new IPEndPoint(LocalIpAddress, i);
    
                        MulticastSocket.Bind(localEndPoint);
                        return;
                    }
                    catch (SocketException e)
                    {
                        Console.WriteLine(e);
                    }
                }
            }
    
            private const int MaxByteLength = 1024;
    
            #region IDisposable Support
    
            private bool _disposedValue = false; // 要检测冗余调用
    
            private void Dispose(bool disposing)
            {
                if (!_disposedValue)
                {
                    if (disposing)
                    {
                    }
    
                    MulticastSocket.Dispose();
    
                    ReceivedMessage = null;
                    MulticastAddress = null;
    
                    _disposedValue = true;
                }
            }
    
            // 添加此代码以正确实现可处置模式。
            public void Dispose()
            {
                // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
                Dispose(true);
                GC.SuppressFinalize(this);
            }
    
            #endregion
    
            private void OnReceivedMessage(string e)
            {
                ReceivedMessage?.Invoke(this, e);
            }
        }
  • 相关阅读:
    0101
    正则表达式 re模块
    经典算法>>mor-c3 / 删除排序
    网络编程
    面向对象>>类(三大特性:继承多态封装)>>反射,内置函数/方法,
    经典算法>冒泡 和二分法
    Apollo 5.0 障碍物行为预测技术
    一种新颖鲁棒的自动驾驶车辆换道轨迹规划方法
    自动驾驶中轨迹规划的探索和挑战
    Lattice Planner规划算法
  • 原文地址:https://www.cnblogs.com/lindexi/p/12086183.html
Copyright © 2020-2023  润新知