UDP: SSDP Search

Overview

This tutorial demonstrates how to use Mongoose Library for UDP communication, by performing an SSDP search.

SSDP (Simple Service Discovery Protocol) is used for advertisement and discovery of network services; serving as the lowest layer of the discovery protocol stack for the UPnP (Universal Plug and Play) architecture.

  1. We send an UDP datagram to the multicast address 239.255.255.250 on port 1900; this datagram contains an SSDP M-SEARCH request
  2. UPnP devices and control points listen to that address and port, so any one of these having the requested capabilities will answer. Responses are unicast
  3. We receive those responses as UDP datagrams
  4. Since SSDP is based on HTTP/1.1 (RFC2616), we can parse those responses and print some contents

Build and run

  • Follow the Build Tools tutorial to setup your development environment.
  • Start a terminal in the project directory; if you've not already done so, clone the Mongoose Library repo
    git clone https://github.com/cesanta/mongoose
    
  • Build the example, this will also start the example:
    cd mongoose/examples/udp-ssdp-search
    make clean all
    
  • Observe the log
    181baf18d6d 2 main.c:40:tfn             Sending M-SEARCH
    181baf18dcf 2 main.c:16:fn              Got a response
        Server -> DLNADOC/1.50 Linux/3.10.104 UPnP/1.0 RKDLNALib/2.0
        Location -> http://192.168.69.246:38388/deviceDescription/MediaRenderer
    
    The example searches for media renderers, if you have one, it should show up. (Windows users: see below)

How it works

  • The event manager is in charge of processing network communications and timing, calling the event handler functions accordingly. It is called repeatedly at the main loop.
int main(void) {
  struct mg_mgr mgr;
  mg_mgr_init(&mgr);
  ...
  while (true) mg_mgr_poll(&mgr, 200);
  mg_mgr_free(&mgr);
  return 0;
}
  • We create a connection by calling mg_connect(). This function will process the URL and return a connection handler or NULL on failure. The function fn will be our event handler function.
static const char *s_ssdp_url = "udp://239.255.255.250:1900";

int main(void) {
  ...
  c = mg_connect(&mgr, s_ssdp_url, fn, NULL);
  • We start a repeatable timer, so we can perform the search every 2 seconds. The function tfn will be our event handler function, and we pass a pointer to our connection, c, as its argument.
  mg_timer_add(&mgr, 2000, MG_TIMER_REPEAT | MG_TIMER_RUN_NOW, tfn, c);
  • When the URL is resolved to an address and port, the connection event handler will receive an MG_EV_RESOLVEevent. We save this information.
  • The timer event handler sends the request to the connection using mg_printf(). We search for media renderers, but we could search for example for root devices: ST: upnp:rootdevice or any SSDP-capable device: ST: ssdp:all
  • UPnP devices and control points listen to that address and port. If there is one having the requested capabilities, it will answer with a unicast UDP datagram. The connection event handler will then receive an MG_EV_READevent. Since the protocol is based on HTTP/1.1 (RFC2616), we can parse those responses and print relevant information.
  • Each response will fill c->rem with the sender information, so we can be able to send something back. Since we need to send the next query to the multicast address, we recover the information we saved before

Browse latest code

Windows users

If the multicast UDP datagrams are sent to the loopback interface, you will only see responses if you actually have a renderer on your machine listening on that interface. In that case, to reach other hosts in your newtwork, configure the socket to use your physical interface by adding these lines to the MG_EV_RESOLVE handler:

  uint32_t ifip = inet_addr("192.168.69.16"); // use your network interface address
  setsockopt((MG_SOCKET_TYPE) (size_t) c->fd, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&ifip, sizeof(ifip));