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 然后绑定到端口

  1. private IPAddress LocalIpAddress { set; get; } = IPAddress.Any;
  2.  
  3. private Socket MulticastSocket { get; }
  4.  
  5. private const int MulticastPort = 15003;
  6.  
  7. private void TryBindSocket()
  8. {
  9. for (var i = MulticastPort; i < 65530; i++)
  10. {
  11. try
  12. {
  13. EndPoint localEndPoint = new IPEndPoint(LocalIpAddress, i);
  14.  
  15. MulticastSocket.Bind(localEndPoint);
  16. return;
  17. }
  18. catch (SocketException e)
  19. {
  20. Console.WriteLine(e);
  21. }
  22. }
  23. }

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

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

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

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

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

接收方法和接收其他相同

  1. private void ReceiveBroadcastMessages()
  2. {
  3. // 接收需要绑定 MulticastPort 端口
  4. var bytes = new byte[MaxByteLength];
  5. EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
  6.  
  7. try
  8. {
  9. while (true)
  10. {
  11. var length = MulticastSocket.ReceiveFrom(bytes, ref remoteEndPoint);
  12.  
  13. Console.WriteLine(Encoding.UTF8.GetString(bytes, 0, length));
  14. }
  15. }
  16. catch (Exception e)
  17. {
  18. Console.WriteLine(e.ToString());
  19. }
  20. }

接收和发送都是二进制

  1. /// <summary>
  2. /// 发送组播
  3. /// </summary>
  4. /// <param name="message"></param>
  5. public void SendBroadcastMessage(string message)
  6. {
  7. try
  8. {
  9. var endPoint = new IPEndPoint(MulticastAddress, MulticastPort);
  10. var byteList = Encoding.UTF8.GetBytes(message);
  11.  
  12. MulticastSocket.SendTo(byteList, endPoint);
  13. }
  14. catch (Exception e)
  15. {
  16. Console.WriteLine("\n" + e);
  17. }
  18. }

所有代码

  1. internal class PeerMulticastFinder : IDisposable
  2. {
  3. /// <inheritdoc />
  4. public PeerMulticastFinder()
  5. {
  6. MulticastSocket = new Socket(AddressFamily.InterNetwork,
  7. SocketType.Dgram,
  8. ProtocolType.Udp);
  9. MulticastAddress = IPAddress.Parse("230.138.100.2");
  10. }
  11.  
  12. /// <summary>
  13. /// 寻找局域网设备
  14. /// </summary>
  15. public void FindPeer()
  16. {
  17. // 实际是反过来,让其他设备询问
  18.  
  19. StartMulticast();
  20.  
  21. var ipList = GetLocalIpList().ToList();
  22. var message = string.Join(';',ipList);
  23. SendBroadcastMessage(message);
  24. // 先发送再获取消息,这样就不会收到自己发送的消息
  25. ReceivedMessage += (s, e) => { Console.WriteLine($"找到 {e}"); };
  26. }
  27.  
  28. /// <summary>
  29. /// 获取本地 IP 地址
  30. /// </summary>
  31. /// <returns></returns>
  32. private IEnumerable<IPAddress> GetLocalIpList()
  33. {
  34. var host = Dns.GetHostEntry(Dns.GetHostName());
  35. foreach (var ip in host.AddressList)
  36. {
  37. if (ip.AddressFamily == AddressFamily.InterNetwork)
  38. {
  39. yield return ip;
  40. }
  41. }
  42. }
  43.  
  44. /// <summary>
  45. /// 组播地址
  46. /// <para/>
  47. /// 224.0.0.0~224.0.0.255为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其它地址供路由协议使用;
  48. /// <para/>
  49. /// 224.0.1.0~224.0.1.255是公用组播地址,可以用于Internet;
  50. /// <para/>
  51. /// 224.0.2.0~238.255.255.255为用户可用的组播地址(临时组地址),全网范围内有效;
  52. /// <para/>
  53. /// 239.0.0.0~239.255.255.255为本地管理组播地址,仅在特定的本地范围内有效。
  54. /// </summary>
  55. public IPAddress MulticastAddress { set; get; }
  56.  
  57. private const int MulticastPort = 15003;
  58.  
  59. /// <summary>
  60. /// 启动组播
  61. /// </summary>
  62. public void StartMulticast()
  63. {
  64. try
  65. {
  66. // 如果首次绑定失败,那么将无法接收,但是可以发送
  67. TryBindSocket();
  68.  
  69. // Define a MulticastOption object specifying the multicast group
  70. // address and the local IPAddress.
  71. // The multicast group address is the same as the address used by the server.
  72. // 有多个 IP 时,指定本机的 IP 地址,此时可以接收到具体的内容
  73. var multicastOption = new MulticastOption(MulticastAddress, IPAddress.Parse("172.18.134.16"));
  74.  
  75. MulticastSocket.SetSocketOption(SocketOptionLevel.IP,
  76. SocketOptionName.AddMembership,
  77. multicastOption);
  78. }
  79. catch (Exception e)
  80. {
  81. Console.WriteLine(e.ToString());
  82. }
  83.  
  84. Task.Run(ReceiveBroadcastMessages);
  85. }
  86.  
  87. /// <summary>
  88. /// 收到消息
  89. /// </summary>
  90. public event EventHandler<string> ReceivedMessage;
  91.  
  92. private void ReceiveBroadcastMessages()
  93. {
  94. // 接收需要绑定 MulticastPort 端口
  95. var bytes = new byte[MaxByteLength];
  96. EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
  97.  
  98. try
  99. {
  100. while (!_disposedValue)
  101. {
  102. var length = MulticastSocket.ReceiveFrom(bytes, ref remoteEndPoint);
  103.  
  104. OnReceivedMessage(Encoding.UTF8.GetString(bytes, 0, length));
  105. }
  106. }
  107. catch (Exception e)
  108. {
  109. Console.WriteLine(e.ToString());
  110. }
  111. }
  112.  
  113. /// <summary>
  114. /// 发送组播
  115. /// </summary>
  116. /// <param name="message"></param>
  117. public void SendBroadcastMessage(string message)
  118. {
  119. try
  120. {
  121. var endPoint = new IPEndPoint(MulticastAddress, MulticastPort);
  122. var byteList = Encoding.UTF8.GetBytes(message);
  123.  
  124. if (byteList.Length > MaxByteLength)
  125. {
  126. throw new ArgumentException($"传入 message 转换为 byte 数组长度太长,不能超过{MaxByteLength}字节")
  127. {
  128. Data =
  129. {
  130. { "message", message },
  131. { "byteList", byteList }
  132. }
  133. };
  134. }
  135.  
  136. MulticastSocket.SendTo(byteList, endPoint);
  137. }
  138. catch (Exception e)
  139. {
  140. Console.WriteLine("\n" + e);
  141. }
  142. }
  143.  
  144. private IPAddress LocalIpAddress { set; get; } = IPAddress.Any;
  145.  
  146. private Socket MulticastSocket { get; }
  147.  
  148. private void TryBindSocket()
  149. {
  150. for (var i = MulticastPort; i < 65530; i++)
  151. {
  152. try
  153. {
  154. EndPoint localEndPoint = new IPEndPoint(LocalIpAddress, i);
  155.  
  156. MulticastSocket.Bind(localEndPoint);
  157. return;
  158. }
  159. catch (SocketException e)
  160. {
  161. Console.WriteLine(e);
  162. }
  163. }
  164. }
  165.  
  166. private const int MaxByteLength = 1024;
  167.  
  168. #region IDisposable Support
  169.  
  170. private bool _disposedValue = false; // 要检测冗余调用
  171.  
  172. private void Dispose(bool disposing)
  173. {
  174. if (!_disposedValue)
  175. {
  176. if (disposing)
  177. {
  178. }
  179.  
  180. MulticastSocket.Dispose();
  181.  
  182. ReceivedMessage = null;
  183. MulticastAddress = null;
  184.  
  185. _disposedValue = true;
  186. }
  187. }
  188.  
  189. // 添加此代码以正确实现可处置模式。
  190. public void Dispose()
  191. {
  192. // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
  193. Dispose(true);
  194. GC.SuppressFinalize(this);
  195. }
  196.  
  197. #endregion
  198.  
  199. private void OnReceivedMessage(string e)
  200. {
  201. ReceivedMessage?.Invoke(this, e);
  202. }
  203. }

2019-10-30-C#-dotnet-core-局域网组播方法的更多相关文章

  1. dotnet core 获取 MacAddress 地址方法

    本文告诉大家如何在 dotnet core 获取 Mac 地址 因为在 dotnetcore 是没有直接和硬件相关的,所以无法通过 WMI 的方法获取当前设备的 Mac 地址 但是在 dotnet c ...

  2. 【2019.10.30】SDN上机第1次作业

    用字符命令搭建如下拓扑,要求写出命令 题目一: 字符命令如下: 题目二: 字符命令如下: 利用可视化工具搭建如下拓扑 要求支持OpenFlow 1.0 1.1 1.2 1.3,设置h1(10.0.0. ...

  3. 2019.10.30 csp-s模拟测试94 反思总结

    头一次做图巨的模拟题OWO 自从上一次听图巨讲课然后骗了小礼物以后一直对图巨印象挺好的233 T1: 对于XY取对数=Y*log(x) 对于Y!取对数=log(1*2*3*...*Y)=log1+lo ...

  4. dotnet core 通过 frp 发布自己的网站

    很多时候写出来的网站只能自己内网访问,本文告诉大家如何通过 Frp 将自己的 asp dotnet core 网站发布到外网,让小伙伴访问自己的网站 通过 frp 的方式,可以解决自己的服务器性能太差 ...

  5. asp dotnet core 支持客户端上传文件

    本文告诉大家如何在 asp dotnet core 支持客户端上传文件 新建一个 asp dotnet core 程序,创建一个新的类,用于给客户端上传文件的信息 public class Kanaj ...

  6. Java单播、组播(多播)、广播的简单实现

    简介 单播有TCP和UDP两种实现,组播(多播)和广播只有UDP一种实现.单播和广播基本一样,只是广播的数据包IP为广播IP.   单播 DatagramSocket和DatagramPacket 服 ...

  7. Alpha冲刺(7/10)——2019.4.30

    所属课程 软件工程1916|W(福州大学) 作业要求 Alpha冲刺(7/10)--2019.4.30 团队名称 待就业六人组 1.团队信息 团队名称:待就业六人组 团队描述:同舟共济扬帆起,乘风破浪 ...

  8. ubuntu15.10 或者 16.04 或者 ElementryOS 下使用 Dotnet Core

    这里我们不讲安装,缺少libicu52自行安装. 安装完成后使用dotnet restore或者build都会失败,一是报编译的dll不适合当前系统,二是编译到ubuntu16.04文件夹下会产生一些 ...

  9. 使用 dotnet core 和 Azure PaaS服务进行devOps开发(Web API 实例)

    作者:陈希章 发表于 2017年12月19日 引子 这一篇文章将用一个完整的实例,给大家介绍如何基于dotnet core(微软.NET的最新版本,支持跨平台,跨设备的应用开发,详情请参考 https ...

随机推荐

  1. 数组的方法之(Array.prototype.filter() 方法)

    filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素.     注意: filter() 不会对空数组进行检测.     注意: filter() 不会改变原始 ...

  2. GitHub的注册与使用

    1. 注册账号: 地址: https://github.com/输入账号.邮箱.密码,然后点击注册按钮. 2. 初始设置 注册完成后,选择Free免费账号完成设置 3.验证账号 新建一个仓库 发现邮箱 ...

  3. Django 自定义auth_user

    1 导入AbstractUser from django.contrib.auth.models import AbstractUser 1 2 创建类UserProfile并继承AbstractUs ...

  4. OpenCV灰度化图像

    OpenCV2版本号非常多函数发生了变化.比如二值化,其演示样例: void CmyMFC2Dlg::OnBnClickedButton1() { // TODO: Add your control ...

  5. 客户端connect返回错误显示No route to host

    务器程序运行起来后,客户端connect返回错误显示No route to host,但是两台机子能ping通 是firewall的问题, services iptables stop应该就ok了

  6. 使用Fiddler抓取到的“姐夫酷”API接口

    下午本来准备抓取些网页视频地址,做一个小的视频app,用来学习ijkplayer,无意中发现了一个app--姐夫酷,这是一个很简单的网页,它也有相应的一个比较简单的android app. 于是心血来 ...

  7. 洛谷 P2146 [NOI2015]软件包管理器 树链剖分

    目录 题面 题目链接 题目描述 输入输出格式 输入格式: 输出格式: 输入输出样例 输入样例#1: 输出样例#1: 输入样例#2: 输出样例#2: 说明 说明 思路 AC代码 总结 题面 题目链接 P ...

  8. python系列之(5)PyMySQL的使用

    简介 PyMySQL是在 Python3.x 版本中用于连接 MySQL 服务器的一个库,Python2中是使用mysqldb. 安装 pip3 install pymysql 创建连接 #!/usr ...

  9. IDEA入门(1)--lombok和Junit generator2插件的运用

    前言 最近在慕课网看到了一些视频,准备从0开始做一个电商网站.视频中的大牛用的java的IDE都是IDEA,让我很纠结.从as到MyEclipse,好不容易稍微熟悉了一下MyEclipse的基本操作, ...

  10. 2019.8.3 [HZOI]NOIP模拟测试12 C. 分组

    2019.8.3 [HZOI]NOIP模拟测试12 C. 分组 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 刚看这题觉得很难,于是数据点分治 k只有1和2两种,分别 ...