Network Discovery with Titanium
So you have 2+ devices that need to find each other on a local network. At least one of them is a mobile device running iOS or Android (sorry Windows users!). One device needs to be hosting a server to handle connections. How do they go about finding each other? You could display a local IP address on the server and have the client devices manually type in the address, but that is terrible UX. There has to be a better way!
Network Discovery is the answer to this problem. Specifically Multicast DNS (mDNS) over Zero-configuration networking (zeroconf). Fortunately these are open technologies that have some implementation available on most platforms. Unfortunately these implementations come with their own names and rarely do they offer any indication that they support each other. Zeroconf, Bonjour, Avahi, or mDNS can be used mostly interchangeably. They’re all implementations of the same technology.
The most well known zeroconf implementation is Apple’s Bonjour. Bonjour is an excellent implementation of zeroconf, but it is only available on iOS, OSX, and Windows (not Windows Phone!). Appcelerator Titanium provides support and documentation for both the BonjourService and the BonjourBrowser.
We’ve recently been working on a project that has to connect iOS and Android devices to a desktop application running on a Windows machine. Our approach was to host an HTTP server on the Windows machine and broadcast its presence over zeroconf, specifically using Bonjour for Windows. On the mobile device we will search for this service, find an address that we can use to reach it, and then communicate over a standard HTTP connection. All examples below will follow this implementation strategy.
Using the provided Bonjour Browser we can easily find our service:
Perfect! We can find our service in iOS! What about Android? Uh oh. There is nothing provided in the Titanium docs for utilizing zeroconf on Android. Unfortunately the answer is going with a native module. I wasn’t able to find a free solution that is up to date, so I had to write my own.
Because we are just doing discovery I have not packaged my module and made it public. This is a very limited implementation that will only work for our specific use case. I share code snipets below, as well as my notes, which should help anyone trying to implement this.
Network Discovery on Android is challenging for several reasons. Apple likes to present Bonjour like it’s a unique technology, but it’s not. This make it difficult to search for. Instead of just searching for “android bonjour” it may be more useful to search for “android network discovery”, “android mdns”, “android zeroconf”, or “android avahi”. Once you realize what you need to be searching for it becomes clear there is a solution! The first solution to explore is the built in Android NsdManager. Google even offers a decent tutorial on how to use it. Here’s what I did with it:
This exposes two methods, one that starts the search and one that stops it. The idea is to use it like this:
Note that this is different than the built in BonjourBrowser. For our use we wanted to find a single service, retrieve the url, and stop searching once we have found it.
This code does exactly that. Unfortunately it has some issues. First of all, the NsdManager was added in Android 4.1. If you need to support devices lower than that you will need a different library. Second, NsdManager has issues finding services. In my testing I had three services broadcasting over zeroconf on my network. One was the service I was looking for running on a Windows 8 laptop, another was a test service in NodeJS using the mdns package from npm running on a Macbook, and finally a Mac Mini broadcasting our internal CI system portal. Using NsdManager on devices running Android 4.4 or lower I could only find the node server. I tried many scenarios and it was the only server I could find. On Android 5.0.1 or higher I could find all the services without an issue. If your user is on Android 5.0.1 or higher I would recommend the NsdManager approach as seen above. If your user is on Android 4.x or lower I would recommend using another library, jmdns.
The module is used in the exact same way, but relies on jmdns instead.
Jmdns is a little buggy and will not find the service every time. Use NsdManager when you can, but verify that it works for your usage.
These examples have only been for discovering the service and don’t even handle communicating over a socket directly through zeroconf like the BonjourBrowser does. I have also not included code for broadcasting a service on the mobile device itself. Hopefully this code points you in the right direction if those are things you need to do from an Android device with Appcelerator Titanium!
Now that I’ve had a chance to work with zeroconf I love what it brings to the table. It’s great to have devices just find each other on a network with no input from the user. Unfortunately the way Bonjour is presented makes it difficult to find information on how to implement zeroconf on devices that Apple does not directly support.