Back to Home

Search for devices on the network by SSDP using Poco

c ++ · poco · ssdp

Search for devices on the network by SSDP using Poco

  • Tutorial
In this small note-example, I will describe how to find devices on the network using the Simple Service Discovery Protocol ( SSDP ) using the Poco library in C ++.

I will say that the paid full version of Poco includes classes for working with UpnP. But for my purposes, the basic version of Poco, which already knows how to work with UDP, was quite enough.

At the expense of the SSDP protocol, it is quite old. The only normal documentation on it that I could find was a draft of the official specification . With quite a lot of letters. ;-)

The essence of the protocol is as follows:

Send a broadcast request on the network - UDP packet to the address 239.255.255.250, destination port 1900.

The body of the request (package) can be viewed in the source code. I will make a reservation that the only field whose value I may have is ST: it indicates the type of devices from which we want to get an answer.

Since this is the UDP protocol, there is no guaranteed answer here as you might get used to when working with HTTP. HTTP works on a request-response basis.

In our case, simply all devices that announce themselves to the network send a UDP packet in response to the address from which the request was sent, IMPORTANT, the answer does not come to port 1900, but to the port from which the request was sent (Source Port).

Since UPD makes no guarantees other than the integrity of the packages themselves. Then for 3 seconds we will listen to the Socket (port) from which the request was sent.

We collect all the answers, and then parse the answers using regular expressions from the same Poco library.

There is another option, just listen to MulticastSocket, this option is given in the Poco documentation on page 17.
But it did not suit me, because the device I was looking for did not announce itself on the network.

In the request, the ST field may take the following values:

  • upnp: rootdevice
  • ssdp: all

This is for finding all devices. In my case, here I indicate the specific class of devices from which I want to receive. But for the article I left upnp: rootdevice I will

also make a reservation that C ++ is a new language for me.

So:

#include 
#include "Poco/Net/DatagramSocket.h"
#include "Poco/Net/SocketAddress.h"
#include "Poco/Timespan.h"
#include "Poco/Exception.h"
#include "Poco/RegularExpression.h"
#include "Poco/String.h"
using std::string;
using std::vector;
using std::cin;
using std::cout;
using std::endl;
using Poco::Net::SocketAddress;
using Poco::Net::DatagramSocket;
using Poco::Timespan;
using Poco::RegularExpression;
void MakeSsdpRequest(vector& responses,string st = "") {
	if (st.empty()) st = "upnp:rootdevice";
	//if (st.empty()) st = "ssdp:all";
	string message = "M-SEARCH * HTTP/1.1\r\n"
		"HOST: 239.255.255.250:1900\r\n"
		"ST:" + st + "\r\n"
		"MAN: \"ssdp:discover\"\r\n"
		"MX:1\r\n\r\n";
	DatagramSocket dgs;
	SocketAddress destAddress("239.255.255.250", 1900);
	dgs.sendTo(message.data(), message.size(), destAddress);
	dgs.setSendTimeout(Timespan(1, 0));
	dgs.setReceiveTimeout(Timespan(3, 0));
	char buffer[1024];
	try {
		// Здесь можно и бесконечный цикл, так как отвалимся по timeout. Но на всякий ограничиваю 1000 пакетами, так как, если кто-то решит отвечать постоянно, timeout не наступит.
		for (int i = 0; i < 1000; i++) {
			int n = dgs.receiveBytes(buffer, sizeof(buffer));
			buffer[n] = '\0';
			responses.push_back(string(buffer));
		}
	}
	catch (const Poco::TimeoutException &) { }
}
string ParseIP(string str) {
	try {
		RegularExpression re("(location:.*://)([a-zA-Z_0-9\\.]*)([:/])", RegularExpression::RE_CASELESS);
		vector vec;
		re.split(str, 0, vec);
		if (vec.size() > 2) return vec[2];
	}
	catch (const Poco::RegularExpressionException&) { cout << "RegularExpressionException" << endl; }
	return "";
}
int main()
{
	vector ips, responses;
	MakeSsdpRequest(responses);
	for (string response : responses) {
		// Проверяю статус ответа.
		if (response.find("HTTP/1.1 200 OK", 0) == 0) {
			string ip = ParseIP(response);
			if (!ip.empty()) ips.push_back(ip);
		}
	}
	sort(ips.begin(), ips.end());
	ips.erase(unique(ips.begin(), ips.end()), ips.end());
	for (const string& ip : ips) {
		cout << "IP: " << ip << endl;
	}
	cout << "Press Enter" << endl;
	cin.get();
	return  0;
}

Read Next