使用MDNS进行局域网服务发现(.NET Core)

想要服务写的好,配置文件不可少。如果是一个复杂的系统,甚至配置文件都是需要进行动态调整的,做起来好像就不是那么方便了,通常情况下,asp.net core中的IConfiguration只能用来读取,没有提供保存功能,如果真的要操作一下,只能通过另外写方法来写入配置文件。可能是这个玩意设计就是Immutable的吧,总之,很难受。

前言

最近在做一个系统,局域网内工作基于C/S结构,一些配置项目需要从Server端发送到Client端。于是我想的第一件事情,就是给Client一个配置文件,通过Client中指定Server地址,发起通讯,并通过WebAPI,GRPC之类的东西获得数据。

貌似挺完美的,然而,这个系统的Client端是可以有很多个的,一个个配置那不是很麻烦,万一服务器地址改了...不敢想。有没有什么方法可以让Client自动发现Server,然后自动下载配置的?

方案

当然可以自己搭建一个UDP广播服务Server,然后Client端监听广播,收到广播之后即可知道IP地址信息,然后进行后续的数据传输操作。实现起来还是挺简单的,可以参考这个问答

但是我太懒了,我找了找有规范协议的,大概有这么些:

1. WS-Discovery

WS-Discovery(Web Services Dynamic Discovery,WSD)是一种局域网内的服务发现多播协议,WS-Discovery定义了两种基本的实现服务发现机制的操作模式,即Ad-Hoc和Managed。

在Ad-Hoc模式下,客户端在一定的网络范围了以广播的形式发送探测(Probe)消息以搜寻目标服务。在该探测消息中,包含相应的搜寻条件。服务该条件的目标服务在接收到探测消息之后将自身相关的信息(包括地址)回复给作为广播消息发送源的客户端。客户端根据获取到的服务信息,选择适合的服务进行调用。

在Managed模式下,一个维护所有可用目标服务的中心发现代理(Discovery Proxy)被建立起来,客户端只需要将探测消息发送到该发现代理就可以得到相应的目标服务信息。由于在Ad-Hoc模式下的广播探测机制在Managed模式下被转变成单播形式,带来的好处就是极大地减轻了网络负载(Network Traffic)。

这个技术是OASIS标准协议,并且在WCF中有完整实现,对应可以搜索UdpDiscoveryEndpoint就可以找到相关的信息。

最开始就是想使用这个协议的,不过WCF已经被弃用了,.NET Core没有对应的服务端支持,可惜。

2. Consul/ZooKeeper

既然WCF要被淘汰了,后续的替代,微软有一篇文章提到了这两个东西,基本上就是WS-Discovery的Managed方式,提供一个代理用于各种服务进行注册,但是还是需要提前配置这些服务注册服务器的地址,达不到我的要求。

3. MDNS

MDNS就是Multicast DNS,在内网没有DNS服务的时候,可以使用它来进行组播实现DNS。使用UDP协议的5353端口。基于这个协议比较著名的实现就是苹果的Bonjour,也有一个非常有名的zeroconf也是差不多这个意思,mDNS也是一个标准(RFC6762)。

在前面两个都用不了的情况下,只能用这个了。

实现

首先安装nuget包,这个包里面包含有server/Client端。

install-package Makaretu.Dns.Multicast

思路是这样的,基于ServiceDiscovery发布一个服务,并将额外的信息发布到然后监听各种mDNS请求,客户端通过服务名发送查询请求,并定位服务的地址信息,然后发送SRV,A和TXT查询请求获得服务全名,IP地址和额外配置信息。这样就获得了在局域网内的服务信息了。

客户端接收的时候,使用了服务名称作为筛选的依据。

服务发布端

var sd = new ServiceDiscovery();
//发布一个服务,服务名称是有讲究的,一般都是_
var p = new ServiceProfile("ipfs1", "_ipfs-discovery._udp", 5010);
p.AddProperty("connstr", "Server");
//必须要设置这一项,否则不解析TXT记录
sd.AnswersContainsAdditionalRecords = true;
sd.Advertise(p);
//sd.Announce(p);
Console.ReadKey();
sd.Unadvertise();

服务调用端

static void Main(string[] args)
{
var mdns = new MulticastService();
var sd = new ServiceDiscovery(mdns); sd.ServiceInstanceDiscovered += (s, e) =>
{
if (e.Message.Answers.All(w => !w.Name.ToString().Contains("ipfs1"))) return;
Console.WriteLine($"service instance '{e.ServiceInstanceName}'"); // Ask for the service instance details.
mdns.SendQuery(e.ServiceInstanceName, type: DnsType.SRV);
}; mdns.AnswerReceived += (s, e) =>
{
if (e.Message.Answers.All(w => !w.Name.ToString().Contains("ipfs1"))) return;
// Is this an answer to a service instance details?
var servers = e.Message.Answers.OfType<SRVRecord>();
foreach (var server in servers)
{
Console.WriteLine($"host '{server.Target}' for '{server.Name}'"); // Ask for the host IP addresses.
mdns.SendQuery(server.Target, type: DnsType.A);
//mdns.SendQuery(server.Target, type: DnsType.AAAA);
} // Is this an answer to host addresses?
var addresses = e.Message.Answers.OfType<AddressRecord>();
foreach (var address in addresses)
{
if (address.Address.AddressFamily== AddressFamily.InterNetwork)
Console.WriteLine($"host '{address.Name}' at {address.Address}");
}
// Get connectionstring from DNS TXT record.
var txts = e.Message.Answers.OfType<TXTRecord>();
foreach (var txt in txts)
{
//“connstr=Server”,获得对应connstr值
Console.WriteLine($"{txt.Strings.Single(w => w.Contains("connstr")).Split('=')[1]}");
//Console.WriteLine($"host '{address.Name}' at {address.Address}");
}
}; try
{
mdns.Start();
sd.QueryServiceInstances("_ipfs-discovery._udp");
Console.ReadKey();
}
finally
{
sd.Dispose();
mdns.Stop();
}
}

运行效果如下:

参考资料

使用MDNS进行局域网服务发现(.NET Core)的更多相关文章

  1. 发现AspNet.Core版本控制库Bug一枚,你还想入坑?

    我,博客写作小白一枚,注册账号多年却未曾留下只言片语,在潜水的这些年里从大家的博客中收获了很多新的知识忽觉惶恐心有不安,是时候给大家分享一些我的经验和教训了.嗯嗯,实话告诉大家前面的话的都是来凑字数的 ...

  2. .NET Core微服务之基于Steeltoe使用Eureka实现服务注册与发现

    Tip: 此篇已加入.NET Core微服务基础系列文章索引 =>  Steeltoe目录快速导航: 1. 基于Steeltoe使用Spring Cloud Eureka 2. 基于Steelt ...

  3. ASP.NET Core 折腾笔记二:自己写个完整的Cache缓存类来支持.NET Core

    背景: 1:.NET Core 已经没System.Web,也木有了HttpRuntime.Cache,因此,该空间下Cache也木有了. 2:.NET Core 有新的Memory Cache提供, ...

  4. 详解Session分布式共享(.NET CORE版)

    一.前言&回顾 在上篇文章Session分布式共享 = Session + Redis + Nginx中,好多同学留言问了我好多问题,其中印象深刻的有:nginx挂了怎么办?采用Redis的S ...

  5. EntityFramework Core 2.0执行原始查询如何防止SQL注入?

    前言 接下来一段时间我们来讲讲EntityFramework Core基础,精简的内容,深入浅出,希望为想学习EntityFramework Core的童鞋提供一点帮助. EntityFramewor ...

  6. 开源纯C#工控网关+组态软件(十)移植到.NET Core

    一.   引子 写这个开源系列已经十来篇了.自从十年前注册博客园以来,关注了张善友.老赵.xiaotie.深蓝色右手等一众大牛,也围观了逗比的吉日嘎啦.精密顽石等形形色色的园友.然而整整十年一篇文章都 ...

  7. [翻译 EF Core in Action 1.11] 何时不应该使用EF Core

    Entity Framework Core in Action Entityframework Core in action是 Jon P smith 所著的关于Entityframework Cor ...

  8. 你所不知道的ASP.NET Core MVC/WebApi基础系列(二)

    前言 好久没冒泡了,算起来估计有快半年没更新博客了,估计是我第一次停更如此之久,人总有懒惰的时候,时间越长越懒惰,但是呢,不学又不行,持续的惰性是不行dei,要不然会被时光所抛弃,技术所淘汰,好吧,进 ...

  9. 搭建一个舒适的 .NET Core 开发环境

    最近,一直在往.Net Core上迁移,随着工作的深入,发现.Net Core比.Net Framework好玩多了.不过目前还在windows下开发,虽然VisualStudio是宇宙第一神器,但是 ...

随机推荐

  1. bugkuCTFWEB部分WP

    前言 之前做的今天整理下 这个有点乱不建议大家参考我的wp 这个主要是自己看的.部分的我做的时候打不开就没写. 练手地址:https://ctf.bugku.com 矛盾 矛盾考察的是PHP弱类型首先 ...

  2. 深度解析:java必须掌握的知识点——类的重用

    类继承的概念和语法 类继承的概念 根据已有类来定义新类,新类拥有已有类的所有功能. Java只支持类的单继承,每个子类只能有一一个直接超类(父类). 超类是所有子类的公共属性及方法的集合,子类则是超类 ...

  3. ABBYY FineReader 15 新增编辑表格单元格功能

    ABBYY FineReader 15(Windows系统)新增编辑表格单元格功能,在PDF文档存在表格的前提下,可将表中的每个单元格作为单独的文字块进行单独编辑,单元格内的编辑不会影响同一行中其他单 ...

  4. 3种终极方法,彻底解决CDR不显示缩略图!

    站长所在的印刷出版行业,一般都是使用版本较低的CDR软件,以便更好的兼容出版厂,不然新版本的文件发厂出片时却打不开,而转低版本的话又容易出错.从最开始的 CorelDRAW 9 到现在的 CORELD ...

  5. FL Studio录制面板知识讲解

    FL Studio录制面板可以设置与录制有关的选项,它还有一个用来设置音符对齐的全局吸附选择器.刚接触水果这款音乐制作软件的同学通常不是很清楚这里的知识的,下面小编就给大家讲解一下. 1.首先,我们来 ...

  6. Word文档数据被误删了怎么办,还能恢复吗

    很多时候由于时间紧张或者是思路不想被打断,我们在编辑Word时不能及时的手动保存,一旦遇到电脑意外断电的情况可能就会导致编辑好的Word文档内容丢失.或者是文档编辑好了之后,Word提示是否保存时,误 ...

  7. ubuntu安装php的 mongodb扩展

    wget https://pecl.php.net/get/mongodb-1.2.6.tgztar -zxvf mongodb-1.2.6.tgz cd mongodb-1.2.6 /usr/bin ...

  8. spring mvc 文件上传报“由于没有提供multi-part配置,无法处理parts”

    在使用springMVC进行文件上传时,报了HTTP 500的错. 才发现原来是在springmvc.xml配置文件中,multipart中的id写错了. 错误代码: <!-- 配置文件解析器- ...

  9. 深度优先遍历&广度优先遍历

    二叉树的前序遍历,中序遍历,后序遍历 树的遍历: 先根遍历--访问根结点,按照从左至右顺序先根遍历根结点的每一颗子树. 后根遍历--按照从左至右顺序后根遍历根结点的每一颗子树,访问根结点. 先根:AB ...

  10. android studio很好用的插件