使用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. 【鸿蒙开发板试用报告】用OLED板实现FlappyBird小游戏(上)

    总是做各种Demo,是时候做个什么小应用来练练手了.踌躇了很久,果然还是搞个小游戏才有意思.想到几年前风靡全球的FlappyBird,一个屏幕一个按钮就足够了,正好适合.OLED屏幕.按键的驱动已经有 ...

  2. DWVA-XSS部分练手闯关

    前言 关于XSS基础内容请查看:https://www.cnblogs.com/xhds/p/12239527.html 实验平台采用DWVA  v1.10 XSS(Reflected)反射性XSS漏 ...

  3. mysql case when语句的使用

    case具有两种格式.简单case函数和case搜索函数. 简单函数 CASE [col_name] WHEN [value1] THEN [result1]-ELSE [default] END 搜 ...

  4. guitar pro系列教程(一):Guitar Pro主界面之记谱功能的详细解析【上】

    相信弹吉他的朋友们对guitar pro这款软件并不陌生,也有很多朋友用它来看谱制谱.而GP有很多实用功能,能够使我们看谱更清晰,制谱更便捷,所以让我们一起来看看吧 Guitar Pro对初学作曲,特 ...

  5. 在Mac上也能轻松拥有Windows应用程序的简便方法

    一般而言,如果我们想要在Windows的环境下下载一款软件那是件很方便的事情.只要我们登陆软件的官网进行下载即可.但是如果我们使用的是Mac OS系统,很多用户就会发现,许多软件会出现不兼容的情况. ...

  6. kakafka - 为CQRS而生fka - 为CQRS而生

    前段时间跟一个朋友聊起kafka,flint,spark这些是不是某种分布式运算框架.我自认为的分布式运算框架最基础条件是能够把多个集群节点当作一个完整的系统,然后程序好像是在同一台机器的内存里运行一 ...

  7. Elasticsearch实现搜索推荐词

    本篇介绍的是基于Elasticsearch实现搜索推荐词,其中需要用到Elasticsearch的pinyin插件以及ik分词插件,代码的实现这里提供了java跟C#的版本方便大家参考. 1.实现的结 ...

  8. iOS中如何使定时器NSTimer不受UIScrollView滑动所影响

    以下是使用 scheduledTimerWithTimeInterval 方法来实现定时器 - (void)addTimer { NSTimer scheduledTimerWithTimeInter ...

  9. LeetCode 029 Divide Two Integers

    题目要求:Divide Two Integers Divide two integers without using multiplication, division and mod operator ...

  10. C和指针---结构和联合

    一.结构 1.C提供了两种类型的聚合数据类型---数组.结构.数组是相同类型的元素集合,它的每个元素长度相同,故可以通过下标引用或指针间接访问来选择的;而结构可以把不同类型的值存储在一起,由于结构的成 ...