Abstract
This post describes my implementation of Network Service Discovery API for any modern browser. At this time, there is the bare minimum information, since the target audience is specialists.
Introduction
I have implemented the Network Service Discovery API [link] on top of UPnP and Bonjour as a Java proxy for any modern browser, coupled with a JS library. The JS library handles the connection with the Java proxy through WebSocket and JSON messages.
This has been tested on PCs and Macs, with recent versions of Chrome, Firefox, Safari and Opera. It also works on Windows 7/IE10. Linux should not be a problem, but was not tested.
Compliance
As far as I understand, the UPnP implementation of NSD is compliant but for the name field which I chose not to implement as the UPnP serviceId, because that does not work in practice, but as the concatenation of the important part of the service type (after :service: and before :1) with the device friendly name. If the device friendly name is not included somewhere in one of the fields of NetworkService, there is no way to present to the user a meaningful list of services, i.e. a list which allows to discriminate services of similar types running on different devices.
The Bonjour implementation is compliant but for the config field, which is set to the device friendly name because I do not have access to the string value mentioned in NSD (or I did not understand the reference).
Extensions: NSD++
A messaging API is implemented in a way that is completely independent from the underlying protocol. It matches the UPnP messaging capability but does not cover events. When a service is discovered, NSD++ can “bind” to that service and returns a service implementation object, which contains one function per service action.
service = NSDPlusPlus.bindService(networkServices[i].id)
...
service.lightSwitch(true); // from Example 2, call action lightSwitch
For UPnP, the extended messaging is implemented on top of UPnP messaging. It is compatible with any UPnP service, even from outside.
For Bonjour, the extended messaging is implemented on top of TCP Sockets and JSON. It is restricted to communicating with Bonjour services advertized by NSD++ web apps.
A service advertizing API is implemented with a service description similar to SDCP but in JSON. An example follows:
var localService = {
"type": "communicationtest",
"protocol": "upnp",
"actionList": [{
"name": "lightSwitch",
"args": [{ "name": "newTargetValue", "dir": "in" },
{ "name": "response", "dir": "out" }]
}]};
For UPnP, the JSON description is translated to SDCP.
For Bonjour, the JSON description is used as is: it is placed in a web server, and its URL is stored in the Desc property of the DNS record of the service.
Installing the Software
Here is a link to the executable jar for the NSD proxy. Run the agent in a terminal with the command line:
java -jar nsd-agent.jar
Running the agent in the terminal is necessary if you want to see the error log. Run the agent before you load any of the examples in a browser.
Here is a link to the examples. Unpack the zip in the document root of a web server. In the following, I assume the URL for the examples is “http://localhost/nsd/”.
Compiling the Software
The project structure is compatible with Maven, and a pom.xml is provided.
Example 1: Discovery @ http://localhost/nsd/discovery.html
The discovery example allows you to discover services. There is a service type field in which you enter the type of service you want to look for. Then you choose to press the blue “Get” button for an exact match of the type you entered, or the orange “get*” button to match a substring of the actual service type. The blue “Get” button triggers NSD.getNetworkServices (the standard API). The entered service type has to start with “upnp:” for a UPnP service, and “zeroconf:” for a Bonjour service.
The orange “Get*” button triggers an extension of this API that cannot be implemented directly with the standard API. I believe the extended API is more easily usable for web application authors and allows the design of web applications that are independent of whether the actual service is implemented in UPnP or Bonjour.
Example 2: Expose a Bonjour Service @ http://localhost/nsd/exposeBonjour.html
This web application shows a light bulb (initially off) and expose a Bonjour service with one lightSwitch action. The service type is zeroconf:_communicationtest._tcp.local.
Example 3: Expose a UPnP Service @ http://localhost/nsd/exposeUPnP.html
This web application shows a light bulb (initially off) and expose a UPnP service with one lightSwitch action, which sends a reply in the form of the time when the action was executed.
The service type is upnp:urn:coltram-org:service:communicationtest:1
Example 4: Call a Bonjour Service @ http://localhost/nsd/callBonjour.html
This web application shows two buttons to control the Bonjour service of Example 2.
Example 5: Call a UPnP Service @ http://localhost/nsd/callUPnP.html
This web application shows a text and two buttons to control the UPnP service of Example 3.
The text is “No service found” initially. It changes to “Service found” when the appropriate service has been found. After clicking on one of the two buttons, the UPnP service replies with the time when the action is executed, and that time replaces the text.
Troubleshooting
In all examples, the lower part of the screen is devoted to a debug log.
Messages appears at the top and older messages scroll down.
The first message is “connecting to undefined”: the web application is connecting to the agent. If the agent is not started yet or not accessible, this is the last thing you see.
The second message is “connected” or “UPnP/Bonjour service ‘X’ exposed”: the web app has established a connection to the agent, and NSD can now be used.
Then, in Call* examples, getNetworkServices is called with a callback called CB. When this callback is called, it prints “CB <nb of services>”. Typically, you get a message sequence of “CB 0” (initially, no service found), then “onserviceavailable callback” (NSD tell you one service was found and you need to call getNetworkServices again), then “CB 1” (one service was found).
Other typical problems:
- If you are on a Wifi network, chances are that UPnP and Bonjour are not allowed. Then you will not be able to discover any service. If you have control over the Wifi configuration, you need to enable the UPnP and Bonjour protocols. If you do not have control over the Wifi, you need to negotiate with the Wifi owner.
- If you are using a PC with Windows and you are not admin, then you cannot use the agent, unless you get an admin to allow your java installation to open any port (required for UPnP, Bonjour and WebSocket)
Source
This project is open source. Here is a link to a zip of the source code and here is the github link. The license is LGPL v3.
This NSD agent uses open source libraries:
- Cling, a UPnP implementation from Christian Bauer at Teleal (LGPL).
- JMDNS, a Bonjour implementation from Arthur van Hoff at Strangeberry (Apache v2 License).
- Java_WebSocket, a WebSocket server and client implementation from TooTallNate (MIT License).
- JSON, a JSON codec implementation from json.org (JSON License).
- pygmy, a small web server implementation from Charlie Hubbard (Artistic License).
The examples use Bootstrap (Apache v2 license) and jQuery (MIT License).