第 3 章 - 内部服务缓存说明

NetX Duo mDNS 模块管理两个内部服务缓存:本地服务缓存和对等服务缓存。 本地服务缓存存储与节点上运行的应用程序所提供服务相关的资源记录。 对于传入的查询,如果问题与提供的服务匹配,则 mDNS 将使用存储在本地服务缓存中的应答做出响应。 应用程序通过调用 API nx_mdns_service_add() 来注册服务。 若要删除服务,应用程序将使用 API nx_mdns_service_delete(),此 API 继而发送“再见”消息,然后删除本地服务缓存中的相应条目。

添加服务时,mDNS 会在本地服务缓存中维护至少 3 个资源记录:SRV、PTR 和 TXT。 如果服务类型包括子类型,则可能会添加其他 PTR 资源记录。 例如,应用程序注册一个服务:

*name*._*subtype*._sub._*type*._tcp.local,

将向本地服务缓存添加两个 PTR 资源记录,一个用于:

“*_subtype._*sub*._type._*tcp.local *PTR name.type._*tcp*.*local”

另一个用于:

*“_type._*tcp*.*local *PTR name.type._*tcp*.*local”.

对等服务缓存包含从邻近节点接收的 mDNS 资源记录。 mDNS 模块会收集由网络上的其他节点播发的资源记录,并将收到的信息存储在对等服务缓存中。 当应用程序查询主机 IPv4 或 IPv6 地址等信息时,mDNS 会在对等服务缓存中搜索本地缓存的响应。 当应用程序查询对等方提供的服务时,mDNS 会在整个缓存中搜索相关的 PTR、SRV、TXT 和 IPv4/IPv6 地址记录。 对等服务缓存还存储发送到节点的查询。 例如,应用程序可以通过调用 nx_mdns_service_one_shot_query 来请求特定服务。如果在对等服务缓存中找不到该服务,则 mDNS 会在对等服务缓存中创建查询问题(PTR、SRV 和 TXT)。 这些查询问题会定期发送到网络,直到服务得到解析或超时为止。类似地,应用程序可以使用 API nx_mdns_service_continous_query() 在较长一段时间内请求特定服务(应用程序可以通过使用 API nx_mdns_service_query_stop() 来取消以前发出的连续查询)。 若要在对等服务缓存中搜索特定服务但不向网络发送查询,应用程序可以使用 API nx_mdns_serivce_lookup()。此 API 仅在对等服务缓存中搜索资源记录。

每个资源记录都存储在服务缓存的 NX_MDNS_RR 数据结构中。 资源记录中的字符串的长度可变,因此不会存储在 NX_MDNS_RR 结构中。 资源记录包含一个指针,该指针指向存储字符串的实际内存位置。 字符串表和资源记录共享服务缓存。 资源记录从服务缓存的开头开始存储,然后逐渐存储到缓存的末尾。 字符串表从服务缓存的末尾开始存储,然后逐渐存储到缓存的开头。 字符串表中的每个字符串都有一个长度字段和一个计数器字段。 向字符串表添加字符串时,如果表中已存在相同的字符串,则计数器值会递增,并且不会为该字符串分配内存。 如果无法向服务缓存添加更多资源记录或新字符串,则会将服务缓存视为已满。

应用程序可通过两种方法查找本地网络上提供的服务。 应用程序可以通过一次性查询发出特定的服务查找,也可以启动连续查询以“监视”网络上的活动。 在一次性查询方案中,应用程序必须指定服务类型。 mDNS 会在整个本地服务缓存和对等服务缓存中进行搜索。 如果找到了服务实例,则一次性查询将返回在资源记录中找到的信息。 如果本地服务缓存或对等服务缓存中没有任何记录,mDNS 将发出查询消息。 如果指定了实例名称,则会向本地网络发送具有特定实例名称的任意类型的查询(SRV 和 TXT 类型,格式为“name.type.local”)。 如果未指定实例名称,则会向本地网络发送 PTR 类型的查询。 收到的第一个完整服务将返回到调用方。

连续查询的工作方式有所不同。 连续查询的典型用例是监视本地网络上的特定服务(例如持续查找本地网络上的打印服务)。 在这种情况下,应用程序将对某种类型的服务发出搜索查询(通过 API nx_mdns_service_continious_query)。 调用方通常不会等待特定响应。 对于作为连续查询提交的查询,mDNS 模块将按指数级增长的间隔定期传输查询。 若要停止查询,应用程序必须使用 API nx_mdns_service_query_stop 来停止这些查询中的内部计时器。 查询类型可以为 NULL,在这种情况下,查询类型设置为特殊的 PTR 类型“_services._dns-sd._udp.local”。 此服务类型由 mDNS 定义为用于发现本地网络上所有可用服务的一种方法。 如果提供了实例名称,则会向本地网络发送具有特定实例名称(格式为“name.type.local”)的 ANY 类型查询(查询 SRV 和 TXT 类型)。 如果实例名称为 NULL,则会向本地网络发送 PTR 类型的查询。

所有响应都记录在对等服务缓存中,包括来自未经请求的查询的响应。 稍后,应用程序可以使用 API nx_mdns_service_lookup 从对等服务缓存检索特定服务。

关于碎片化的特别说明:每个 NX_MDNS_RR 结构都具有固定大小,因此在 mDNS 添加和删除资源记录时不会碎片化。 但字符串的长度可变。 删除一个字符串后,该字符串占有的空间将变为可用,如果空间足够,则可用于新字符串。 但此操作会导致内存碎片化。 在不断添加和删除服务的过程中,字符串表会碎片化,以至于即使服务缓存中有足够的未用字节,也无法添加新字符串。 本地应用程序的稳定性和可预测性往往更高。 因此,本地服务缓存不太可能受到碎片化的影响。 另一方面,对等服务缓存会在服务变为可用时持续添加资源记录,或者在网络上的节点删除服务时删除资源记录。 网络上的服务的可用性变化不定,从而导致更频繁地对字符串表执行分配/删除操作。 随着时间的推移,字符串表可能会变得碎片化。 出现这种情况时,简单的解决办法是刷新整个对等服务缓存。 可以通过调用 API nx_mdns_peer_cache_clear() 来执行此操作。请注意,此 API 会自动终止任何未完成的连续查询。