Service Discovery within LAN
Anjan Roy
Posted on April 29, 2019
idea
Let's assume a situation -- You've two devices within same Local Area Network and you want to send a file from deviceA to deviceB.
deviceA -> deviceB
What do you do then ?
Write a server program for deviceA and one client program for deviceB 🤔.
Well this works. But next time when you run these programs, deviceA and deviceB may have different IP addresses or another deviceC may be interested in participating in file transfer, which might eventually lead you to manually updating PEER's IP address 😞.
How about we create an automated device discovery service ?
Sounds great 👏
So let's get to work 🏃
model
We're going to use DatagramSocket, for handling Service Advertising and Service Discovery with in LAN. Implementation will be done using Dart Language and snippets for JavaScript will be provided.
init
First we're going to write Service Advertiser i.e. a UDP server program.
Service Advertiser
Classes required for implementation of proposed functionality.
import 'dart:io' show RawDatagramSocket, RawSocketEvent, InternetAddress, Datagram;
import 'dart:convert' show utf8;
Binding a RawDatagramSocket on InternetAddress.anyIPv4 and port 8000, so that it can listen on all interfaces on port 8000.
RawDatagramSocket.bind(InternetAddress.anyIPv4, 8000).then((datagramSocket) {
// more code coming ...
});
You can see RawDatagramSocket.bind( ... ) returns a Future. We'll enable READ event listener on this socket, so that whenever any other device tries to write something on this socket, a listener will be notified.
datagramSocket.readEventsEnabled = true; // put this line within aforementioned `then()`
Now we'll listen for READ events on this
DatagramSocket. Whenever a new READ event occurs it's fetched using DatagramSocket.receive() and other end get's notified that it has reached a valid Service Advertiser.
datagramSocket.listen((RawSocketEvent event) {
if (event == RawSocketEvent.read) {
// checking whether it's a read event or not
Datagram dg = datagramSocket.receive();
if (dg != null) {
// notifying other end that it has reached a service advertiser
datagramSocket.send(dg.data, dg.address, dg.port);
print('${dg.address}:${dg.port} -- ${utf8.decode(dg.data)}');
}
}
});
PEER's IP address, port number and received data are extracted from Datagram object, received on occurance of READ event.
Received data is decoded back to String from List, using following function from utf8 module.
utf8.decode(dg.data);
Well Service Advertiser is completed 😉.
Service Discoverer
You may have already guessed, this will be simply a UDP client.
Classes required for implementation of service discovery functionality.
import 'dart:io' show RawDatagramSocket, RawSocketEvent, InternetAddress, Datagram;
import 'dart:convert' show utf8;
Again we need to bind a RawDatagramSocket on InternetAddress.anyIPv4 and port 0, which will return Future.
Why port 0 ? 🤔
Well specifying port number 0, lets us use OS chosen port number. In client side we're not that much interested in listening on a predefined port number 😊.
RawDatagramSocket.bind(InternetAddress.anyIPv4, 0).then((datagramSocket) {
// more code coming ...
});
During Service Advertising, we were just listening for READ events on RawDatagramSocket, but now we're also going to enable broadcast, so that this socket can send a broadcast message to 255.255.255.255, which will reach all devices within LAN.
// put these two lines with in previous `then()`
datagramSocket.broadcastEnabled = true;
datagramSocket.readEventsEnabled = true;
Time to send a message on broadcastAddress and port 8000.
datagramSocket.send("io.github.itzmeanjan.transferZ".codeUnits,
InternetAddress("255.255.255.255"), 8000);
Aforementioned message reaches all devices present in LAN listening for READ event on port 8000.
Why port 8000 ? 🤔
Well we're using port 8000 for Service Advertising 😉.
You may be wondering what's "io.github.itzmeanjan.transferZ".codeUnits, this returns List from String, to be sent as broadcastMessage.
Now let's put logic required for listening READ events on RawDatagramSocket. Following snippet is pretty close to the one which we used in Service Advertiser, except the fact that we don't send anything back to Service Advertiser.
datagramSocket.listen((RawSocketEvent event) {
if (event == RawSocketEvent.read) {
Datagram dg = datagramSocket.receive();
if (dg != null) {
print('${dg.address}:${dg.port} -- ${utf8.decode(dg.data)}');
//datagramSocket.close();
// You may consider uncommenting previous line, if you want to explore only one Service Advertiser at a time.
}
}
});
And our Service Discoverer is complete 😃.
run
Time to run it 🏃
Service Advertiser
Run it using
$ dart service_advertiser.dart
Service Discoverer
Now run the servise discoverer using
$ dart service_discoverer.dart
Here's what happened ---
I start Service Advertiser from one terminal, another Service Advertiser from a Flutter mobile app and a Service Discoverer from another terminal on same computer. All these devices are with in same LAN.
First our Service Discoverer sends a broadcastMessage to 255.255.255.255 on port 8000, and starts listening for READ event(s) on RawDatagramSocket. Then Service Advertiser(s) receive READ event notification and responds to it. Response comes to Service Discoverer, it reads that and prints out on screen.
end
And we've successfully implemented nearby service advertising & discovery with Datagram Socket, implemented using Dart Language.
Interested in having this implementation in JavaScript ?
Check here for service_advertiser and service_discoverer.
If you are interested in learning how to use this with a flutter app, you might consider checking transferZ, an android app built with ❤️ using Flutter for sending files to devices with in LAN.
See you in next article. In the mean time you may consider following me on GitHub and Twitter.
Wrap up time ✌️.
Posted on April 29, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.