In this post we will see how data can be transferred between two android devices in the same network. What follows is a bit of theory and implementation in android studio. There is a source code link at the bottom of the page for reference.
In LAN communication a server listens for client requests in one port (with a blocking connection) and client connects and communicates with server using the IP address and port information. This is basic socket communication in client-server architecture.
LAN communication have been around since ages, be it between laptops, desktops or other mobile devices. Socket communication is nothing new in the world on networking. However, there are mainly two problems associated with it:
- Device(s) is/are not aware of the IP address of other device(s).
- Device(s) do not know the port other device(s) is/are listening on.
The 2nd problem mentioned above is solvable by prefixing a port. Say port 35627 is fixed for our particular application, so once we know the IP address we know the port we have fixed for our application. But this has other side-effects. OS usually gets any free port when any application requests it or for its own purposes. If the fixed port is used by any other application and you try to use it, you will get a port already in use exception. To avoid this, the port must be requested dynamically from the OS, and OS will provide you any free port (hence no chances of port already in use exception).
And other device(s) must be aware of this port and IP information for communicating with it.
With NSD (Network service discovery), this very problem is addressed. Communication happens over socket like always, but NSD equips us with ways to share the IP and port information. Here’s what NSD is all about, and how to solve the mentioned problem using NSD.
Android NSD allows our app to identify devices in the same network that offer the services we are requesting. With NSD we can register, discover and connect with our service (or other services) over network. Service is basically some information that is advertised over network. Similar to a broadcast receiver and intent broadcast in android. Device 1 advertises some service say Multi-player chess and device 2 discovers it, connect with it and LAN chess use case solved.
Basic steps involved in NSD are depicted in below figure:
As you can see from above, steps involved in NSD are as follows:
- Application starts and registers a service over network (optional step, avoid if all you want is to discover a service).
- App starts service discovery over network.
- In callback method a service object is received when service is FOUND.
- In callback method a service object is received when service is LOST.
- resolve the found service and extract the port and IP information of the service advertiser.
- Use socket to establish a connection and communicate.
Once port and IP information is retrieved by resolving the found service, the socket communication (yes the ye-old socket communication) is used to transfer data. Lets see these are done in code.
Registering your service
Android’s NsdManager class is used for this. simply call registerService.
NsdServiceInfo serviceInfo = new NsdServiceInfo(); serviceInfo.setPort(port); serviceInfo.setServiceName(mServiceName); serviceInfo.setServiceType(SERVICE_TYPE); mNsdManager.registerService( serviceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener);
An NsdServiceInfo object needs to be created and passed in register service method for registration (don’t miss out the port number there). It holds the service information to be advertised. Service name can be any name you want your service to have, a simple string. Service type is also a string and needs to be in “_<protocol>._<transportLayer>” format. You can choose the type of service from IANA. For the demo I created I simply used an unregistered one “_localdash._tcp“.
Next parameter is protocol, please use the one mentioned in the code, I didn’t find any other protocol. DNS_SD stands for Domain Name System Service Discovery.
The registration listener passed as the last parameter in register service method is callback. It contains 4 methods related to registration. Registration listener’s callback methods are when registration is successful, or failed, or unregistration happens etc. Click on this link to know more.
Callback on Registration listener’s onServiceRegistered() method means service has been successfully advertised. Next is discovering the advertised services.
For discovering services using NSD we use the discoverServices() method (of course).
mNsdManager.discoverServices( SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener)
Here we are telling NsdManager which type of services to discover. The callback here is Discovery listener. This callback informs us when a service is found or lost. It has many callback methods, the one that is important to us is onServiceFound(). Click here to know about other callback methods of discovery listener. For every failure method there is a reason code in parameter. The service found here can be a different service than what we are expecting make sure to verify service type and service name before proceeding. The source for my demo app is shared on github, do check it out to know how to do it (github link). The NsdServiceInfo object in onServiceFound() method cannot be used yet. It needs to be resolved first.
Resolving discovered services
Services can be resolved via resolveService() method of NsdManager. Here is the one liner code for it:
This is called in service found method of discovery listener (after confirming it is our service). The call back Resolve listener has two methods. on service resolved and on resolve failed (with error code). Once service resolved callback is received, we can use the service info object in its parameter to retrieve IP and port information. Here is how to do it:
NsdServiceInfo serviceInfo = //service object in OnServiceResolved() String ipAddress = serviceInfo.getHost().getHostAddress(); int port = serviceInfo.getPort(); //Data sending service DataSender.sendCurrentDeviceData(LocalDashNSD.this, ipAddress, port);
Here we got the service advertisers IP address and port information set by him (or it, the advertiser) when advertising the service. And done!!! socket communication can happen. This happened without any mischievous stuff or hard coding or any magic numbers. Everything is real dynamic.
If you don’t know how to perform basic socket communication or you want to check a sample app for whatever we discussed here check out my demo app code on github.
Let me know what you think. Got any questions? shoot below in comments.
-Kaushal Dhruw (@drulabs twitter/github)
firstname.lastname@example.org (skype: kaushal.dhruw_tal)