I have an application that needs to search and resolve a Bonjour-advertised service whose name is known in advance. Most Bonjour examples I have found related to service discovery are structured more or less like this:
Call browse to detect all services of a given type (for example, this could be _http._tcp)
For each service found, serviceFound is called. Service names are reported here
Call resolve on each service found
For each service resolved, serviceResolved is called
Is it possible with Bonjour to skip the "discovery" stage, since I know in advance the name of the service I want to resolve? Can I just detect and resolve a service with a known name?
1- Answer
Yes, you can start with the 3rd step if you already know the name of the service. This is because this step is performed through a DNS lookup for a SRV record with the name of the service sent to a well-known multicast address. So, no previous information is needed to make this call, and the mDNS responder must be stateless, since the underlying DNS protocol is stateless (each response is bound to a unique request - no state maintained between several requests).
2- Example
Here is an example I've just written with Swift, that has passed tests running on my iPad to find a service running on my Mac Mini.
So, we suppose the domain is local, the service type is _http._tcpand the name of the service is myservice, running on host Mac-mini-de-Alexandre.local and listening to TCP port 8080.
To track informations about the service, for instance its hostname and TCP port, we define a class that implements the NetServiceDelegate protocol:
class MyNetServiceDelegate : NSObject, NetServiceDelegate {
public func netServiceDidResolveAddress(_ sender: NetService) {
print(sender.hostName!, sender.port)
}
}
This new class will be used to instantiate the delegate for a NetService instance.
So, we create a NetService instance corresponding to the service we already know about, that we store in the long-term with a static constant property of some main class:
static let ns = NetService(domain: "local.", type: "_http._tcp.", name: "myservice")
It is stored in the long term because it must not be deallocated before we find our service.
Note that the delegate property in class NetService is declared unowned(unsafe). So, we also need to create a reference to the delegate instance:
static let ns_deleg = MyNetServiceDelegate()
When we want to resolve the service, we may write:
ns.delegate = ns_deleg
ns.resolve(withTimeout: TimeInterval(10))
The delegate instance will be called later (resolve() is a non-blocking method) if the service is found, and in this case, it will print the hostname and port.
Here is the output I got in my Xcode output window:
Mac-mini-de-Alexandre.local. 8080
Finally, note that because of the unowned reference, it would be a mistake to write the following code (the delegate instance would be shortly deallocated):
// bad code -- do not write that -- only here to show a common mistake
ns.delegate = MyNetServiceDelegate()
ns.resolve(withTimeout: TimeInterval(10))
3- Trick to help debugging
Here is a little trick to debug such a mDNS resolution: on a Unix shell (macOS for instance), just run the following line:
dig -p 5353 #224.0.0.251 myservice._http._tcp.local. SRV +short
If an http service with name myservice is running, you will get the host name and port. With my example, you will get the following:
0 0 8080 Mac-mini-de-Alexandre.local.
So, before trying to use the Swift code I've written here, just check that your service is correctly announced with this shell command.
Finally, note that this dig based command only makes one IPv4 mDNS query on each IPv4 network interface, but using the Apple Bonjour API, two groups of mDNS requests are done automatically: one with IPv4 to the multicast destination 224.0.0.251 on each network interface that supports IPv4, and another with IPv6 to the multicast destination ff02::fb on each interface that supports IPv6.
Related
I need to keep track if the device's network IP environment has changed.
I looked at Swift - Get device's WIFI IP Address
I do not necessarily want WiFi IP address only, but any network IP that it is able to communicate (including cellular).
However, there is a corner case: when the internal (non externally routable) addresses like 192.168.x.x become duplicates, but in different subnets.
To make it clear, home network of house A gives me 192.168.1.10; when I join another home network say House B, the home net of house B could also assign me 192.168.1.10.
In this case, how would I track that the environment changed with same IP address?
If you don't actually care about the IP address, I would suggest that you use NWPathMonitor - You can use this to invoke a closure when the available network interfaces change.
I tested it with two different SSIDs on my Wi-Fi (The device gets the same IP address on both, since it is the same network underlying both SSIDs) and it reports a path change when I move from one to the other.
class ViewController: UIViewController {
private var pathMonitor: NWPathMonitor!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.pathMonitor = NWPathMonitor()
self.pathMonitor.pathUpdateHandler = newPath
self.pathMonitor.start(queue: DispatchQueue.global())
}
func newPath(_ newPath: NWPath) -> Void {
print("Network path changed")
print("Is expensive - \(newPath.isExpensive)")
}
}
I will implement the data exchange between the module and the device Sim5360e through UART2. I write in the Lua, AT commands to send / receive information via the UART2 did not find - so use internal devio function.
sio.send("AT+CSCLK=0\r\n")
readAndPrintAtUnswer()
sio.send("AT+CGFUNC=21,1\r\n")
readAndPrintAtUnswer()
sio.send("AT+IPR2=9600\r\n")
readAndPrintAtUnswer()
local msg,count=getHex(str)
local unswer
devio.open(3)
devio.write(3,msg,count)
unswer=devio.read(3,4000)
print(unswer)
devio.close(3)
When connected to a terminal on the PC - see the correct incoming message, the module enters the mode of reading, I send data through the terminal, but after a timeout (4000 ms) unswer = nil. Data from the terminal out exactly correct - checked by another device.
Can you please tell, how do I get an answer to my message?
I want to write a mote-mote radio communication program, and want the receiver acknowledges back to the sender. I know the PacketAcknowledgements is recommended, but there are some questions I'm not sure about this interface.
1. If I use it in the sender mote,should i also uses interface Receive in the module of the sender mote
2. Should I write extra code in the receiver mote? Should I use interface PacketAcknowledgements too?
3. command error_t requestAck(message_t *msg) and command bool wasAcked(message_t *msg) should be used when and where
No.
No.
You need to call requestAck on a packet you're about to send just before calling send from interface AMSend or Send. Be sure to check an error code returned by requestAck, because FAIL indicates that the communication layer doesn't support synchronous acknowledgements (it depends on the radio chip and driver you use). Once the packet is sent, i.e., inside event sendDone (or later), call wasAcked, which returns true if the packet was acknowledged by the receiver.
More info in:
https://github.com/tinyos/tinyos-main/blob/master/tos/interfaces/PacketAcknowledgements.nc
I'm trying to study and understand operations of the Linux tcp/ip stack, specifically how 'ping' sends packets down and receives them.
Ping creates raw socket in AF_INET family, therefore I placed printk in inet_sendmsg() at net/ipv4/af_inet.c to print out the socket protocol name (RAW, UDP etc.) and the address of protocol specific sendmsg function which correctly appears to be raw_sendmsg() from net/ipv4/raw.c
Now, I'm sending a single packet and observe that I'm getting printk form inet_sendmsg() twice.This puzzles me -- is it normal (has something to do with interrupts etc. ?) or there's something broken in the kernel?
Platform - ARM5te, kernel 2.6.31.8
Looking forward to hearing from you !
Mark
I was reading more about erlang:is_port/1 so I decided to test it with several values.
I saw that with normal sockets it replies true if the socket is up and false otherwise (i.e. socket is down).
Can is_port/1 be used also with ssl sockets? I tried but it always returns false.
If you refer to a SSL Socket as the returned value from (for example) ssl:connect/2,3, then the answer is "no". The SSL Sockets in the context of the SSL application are of a sslsocket() type, which, according to the documentation are opaque to the user and definitely not a port. Specifically, they are records:
%% Looks like it does for backwards compatibility reasons
-record(sslsocket, {fd = nil, pid = nil}).