Service Discovery within LAN
Tags: javascript, node, dart, webdev
Published: 2019-04-29 09:57:46

---
title: Service Discovery within LAN
published: true
description: Advertise & Discover nearby Services with in LAN
tags: javascript, node, dart, webdev
---

## 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** :thinking:.

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 :disappointed:.

> How about we create an automated device discovery service ?

> Sounds great :clap:

So let's get to work :running:

## 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.

```dart
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*.

```dart
RawDatagramSocket.bind(InternetAddress.anyIPv4, 8000).then((datagramSocket) {
    // more code coming ...
  });
```

You can see *RawDatagramSocket.bind( ... )* returns a *Future<DatagramSocket>*. 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.

```dart
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*.

```dart
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<int>*, using following function from *utf8* module.

```dart
utf8.decode(dg.data);
```

Well *Service Advertiser* is completed :wink:.

### Service Discoverer
You may have already guessed, this will be simply a UDP client.

Classes required for implementation of service discovery functionality.
```dart
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<DatagramSocket>*.

> Why port 0 ? :thinking:

> 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 :blush:.

```dart
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.

```dart
// put these two lines with in previous `then()`
datagramSocket.broadcastEnabled = true;
datagramSocket.readEventsEnabled = true;
```

Time to send a message on *broadcastAddress* and port 8000.

```dart
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 ? :thinking:

> Well we're using port 8000 for *Service Advertising* :wink:.

You may be wondering what's *"io.github.itzmeanjan.transferZ".codeUnits*, this returns *List<int>* 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*.

```dart
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 :smiley:.

## run
Time to run it :running:

### Service Advertiser
{% gist https://gist.github.com/itzmeanjan/7288e0e9e1be4ae333a4f2761d156ffd %}

Run it using 
```bash
$ dart service_advertiser.dart
```

### Service Discoverer

{% gist https://gist.github.com/itzmeanjan/280bb64b48878571b898c99f274ec8df %}

Now run the servise discoverer using
```bash
$ dart service_discoverer.dart
```

![service_advertiser_and_service_discoverer_running_on_computer](https://thepracticaldev.s3.amazonaws.com/i/5259kfsys6xnu8r479xw.gif)

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](https://github.com/itzmeanjan/transferZ-desktop/blob/master/advertise_service.js) and [service_discoverer](https://github.com/itzmeanjan/transferZ-desktop/blob/master/discover_service.js).

If you are interested in learning how to use this with a flutter app, you might consider checking [transferZ](https://github.com/itzmeanjan/transferZ), an android app built with :heart: 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](https://github.com/itzmeanjan) and [Twitter](https://twitter.com/meanjanry).

Wrap up time :v:.