Some terminology first:
Publisher device - device publishing the NSNetService in the local. domain
Browser device - device browsing for service with NSNetServiceBrowser in the local. domain
The issue:
NSNetServiceBrowserDelegate's didRemoveService is not being called on the browser device when both the publisher and the browser device are connected to the same router and thepublisher device goes off the grid by enabling Airplane mode (I think turning WiFi off will also work). When the publisher and the browser are not connected to the router but are communicating directly (peer-to-peer), the issue doesn't reproduce.
The problem is easily reproducible with the Apple's WiTap samplecode application (that is no longer exists in Apple's sample code 😟, but can be found on the net).
Steps to reproduce:
1. Connect the application to the same router
2. Run the WiTap application on both devices
3. Turn airplane mode on publisher device
Result:
The browser device still displays the publisher service
Publisher code
NSNetService *server = [[NSNetService alloc] initWithDomain:@"local." type:@"_myservice._tcp." name:@"my-id" port:0];
server.includesPeerToPeer = YES;
server.delegate = self;
[server publishWithOptions:NSNetServiceNoAutoRename|NSNetServiceListenForConnections];
self.server = server;
Browser code
NSNetServiceBrowser *browser = [[NSNetServiceBrowser alloc] init];
browser.includesPeerToPeer = YES;
browser.delegate = self;
[browser searchForServicesOfType:@"_myservice._tcp." inDomain:@"local."];
self.browser = browser;
Any ideas & suggestions will be highly appreciated!
TL;DR
I suspect that the problem is not directly related to NSNetService but rather related to Bonjour implementation. The reason I think so is that after force closing the browser app and re-running it (while the publisher device is still in Airplane mode), the browser devicestill shows the service as a result of the didFindService delegate method being called for a service that the publisher doesn't publish anymore.
P.S. The issue reproduces on iOS 12 and below (tested down to iOS 7).
Thanks.
why the issue doesn't reproduce when the devices (publisher and browser) are not connected to a router?
I’d have to dig into the details to be sure, but I suspect it’s because in the peer-to-peer case the interface goes down, and that takes all mDNS records discovered over that interface with it.
Regarding the
DNSServiceReconfirmRecord
call - do you have a reference to a sample code that uses this function?
I have some very old code lying around for this. It’s not in a state I can post, but here’s a snippet:
err = DNSServiceReconfirmRecord(
0,
interfaceIndex,
serviceName,
kDNSServiceType_PTR,
kDNSServiceClass_IN,
(uint16_t) [recordData length],
[recordData bytes]
);
Let’s look at the notable parameters:
-
As with all
<dns_sd.h>
calls, the interface index is the value coming back fromif_nametoindex
. -
serviceName
is a C string holding the FQDN of the Bonjour PTR record. For example, for an SSH service call “Guy Smiley” in the local domain this would be_ssh._tcp.local.
-
recordData
is anNSData
value containing the ‘stale’ value of the PTR record. Contining the above example, this would be the labelsGuy Smiley
,_ssh
,_tcp
, andlocal
, all packed in standard DNS name form (that is, each label prefixed by a length byte, with a trailing 0x00 to indicate the root name), or:0A477579 20536D69 6C657904 5F737368 045F7463 70056C6F 63616C00
.
What do you think of that approach?
Essentially what you’re doing here is adding a bunch of extra traffic to override the default mDNS TTL values. I don’t think that’s a good idea. Even the lowest-level DNS-SD API (DNSServiceRegister
from <dns_sd.h>
) does not let you override these defaults because getting these wrong can really punish your network. Section 10 of RFC 6762 lists these default values and explains how mDNS maintains cache coherency.
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"