2019-10-30-C#-dotnet-core-局域网组播方法「建议收藏」

2019-10-30-C#-dotnet-core-局域网组播方法「建议收藏」titleauthordateCreateTimecategoriesC#dotnetcore局域网组播方法lindexi2019-10-309:0:48+08002019-10-2912:2:46+0800dotnetC#我在微软官网找到了用…

大家好,又见面了,我是你们的朋友全栈君。

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("\n" + 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("\n" + 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);
        }
    }

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/147499.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 游戏数值策划

    游戏数值策划游戏数值-拆解方法篇大家好,我是Alice,一名喜欢捣腾数字的游戏数值策划12345~o(* ̄▽ ̄*)ブ工作中我会经常拆解游戏数值,通过数值理解市面上优秀游戏的设计思路。今天想在这里跟大家分享一下我研究游戏数值的流程。如果各位有什么好方法,也请在评论区留言,期待看到你的想法。我平时拆解数值的流程主要分为六个阶段:准备阶段 数据收集 分析数据规律 提出猜想 根据数据验证猜想 拆解的应用不过在介绍具体流程之前,我想和你们先聊一下,数值拆解的目的。我认为数值拆解的目的大概可以分

    2022年5月5日
    48
  • 计算机网络交换机命令汇总,锐捷交换机常用配置命令汇总,收藏备用![通俗易懂]

    计算机网络交换机命令汇总,锐捷交换机常用配置命令汇总,收藏备用![通俗易懂]一、连接及远程登录用一台计算机作为控制台和网络设备相连接,通过计算机对网络设备进行配置。1、硬件连接把Console线一端连接在计算机的串口上,另一端连接在网络设备的Console口上。按照上面的线序制作一根双绞线,一端通过一个转换头连接在计算机的串口上,另一端连接在网络设备的Console口上。2、软件安装在计算机上安装一个终端仿真软件来登录网络设备。“超级终端”安装方法,通常是使用secure…

    2022年6月15日
    57
  • 像Excel一样使用python进行数据分析

    像Excel一样使用python进行数据分析Excel是数据分析中最常用的工具,本篇文章通过python与excel的功能对比介绍如何使用python通过函数式编程完成excel中的数据处理及分析工作。在Python中pandas库用于数据处理

    2022年7月6日
    18
  • arcpy环境搭建「建议收藏」

    arcpy环境搭建「建议收藏」1.1什么是ArcPy?ArcPy是一个以成功的arcgisscripting模块为基础并继承了arcgisscripting功能进而构建而成的站点包。目的是为以实用高效的方式通过Python执行地理数据分析、数据转换、数据管理和地图自动化创建基础。该包提供了丰富纯正的Python体验,具有代码自动完成功能(输入关键字和点即可获得该关键字所支持的属性和方法的弹出列表;从中选择

    2025年8月1日
    6
  • Gradle的使用教程

    Gradle的使用教程一、相关介绍Gradle是一个好用的构建工具,使用它的原因是:配置相关依赖代码量少,不会像maven一样xml过多 打包编译测试发布都有,而且使用起来方便 利用自定义的任务可以完成自己想要的功能二、安装下载地址http://services.gradle.org/distributions/,下载你所需要对应的版本,我这里下载的是grad…

    2022年6月28日
    27
  • Cisco ACS AAA认证

    Cisco ACS AAA认证ACS认证、授权、审计,以及报文交互

    2022年6月3日
    37

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号