6

I have a Roku device on my network and I want to be able to discover it programically. The official Roku documentation says:

There is a standard SSDP multicast address and port (239.255.255.250:1900) that is used for local network communication. The Roku responds to M-SEARCH queries on this ip address and port.

In order to query for the roku ip address, your program can send the following request using the http protocol to 239.255.255.250 port 1900:

They provide an example using netcat and they say that wireshark can be used to find the result. They also say:

The External Control Protocol enables the Roku to be controlled via the network. The External Control Service is discoverable via SSDP (Simple Service Discovery Protocol). The service is a simple RESTful API that can be accessed by programs in virtually any programming environment.

I have a java program that controls my Roku given its IP Address, and I would like to implement a function that discovers it on the network using this SSDP.

How do I send an M-SEARCH query with java? I have absolutely no conception of how to do this. Is it like an get/post request? If somebody could point me in the right direction I would be very grateful!

John Dorian
  • 1,884
  • 1
  • 19
  • 29

1 Answers1

6

I found a java solution:

/* multicast SSDP M-SEARCH example for 
 * finding the IP Address of a Roku
 * device. For more info go to: 
 * http://sdkdocs.roku.com/display/sdkdoc/External+Control+Guide 
 */

import java.io.*;
import java.net.*;

class msearchSSDPRequest {
    public static void main(String args[]) throws Exception {
        /* create byte arrays to hold our send and response data */
        byte[] sendData = new byte[1024];
        byte[] receiveData = new byte[1024];

        /* our M-SEARCH data as a byte array */
        String MSEARCH = "M-SEARCH * HTTP/1.1\nHost: 239.255.255.250:1900\nMan: \"ssdp:discover\"\nST: roku:ecp\n"; 
        sendData = MSEARCH.getBytes();

        /* create a packet from our data destined for 239.255.255.250:1900 */
        DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, InetAddress.getByName("239.255.255.250"), 1900);

        /* send packet to the socket we're creating */
        DatagramSocket clientSocket = new DatagramSocket();
        clientSocket.send(sendPacket);

        /* recieve response and store in our receivePacket */
        DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
        clientSocket.receive(receivePacket);

        /* get the response as a string */
        String response = new String(receivePacket.getData());

        /* print the response */
        System.out.println(response);

        /* close the socket */
        clientSocket.close();
    }
}
John Dorian
  • 1,884
  • 1
  • 19
  • 29
  • 2
    Wasn't it necessary to add a second \n in the end of the MSEARCH string? It was in my case. – dr.doom Sep 29 '15 at 19:32
  • @dr.doom Oh interesting, it works fine for me without the second newline, but it's good to know that in some cases it may be required – John Dorian Sep 29 '15 at 22:34
  • well, according to the http spec, the Request (section 5) and Response (section 6) messages use the generic message format of RFC 822 [9] for transferring entities (the payload of the message). Both types of message consist of a start-line, zero or more header fields (also known as "headers"), an empty line (i.e., a line with nothing preceding the CRLF) indicating the end of the header fields, and possibly a message-body. So the empty line is mandatory and if not a body is present it marks the end of the message... – dr.doom Sep 29 '15 at 22:43
  • 1
    I had to add "MX: 10" (without quotes) to the MSEARCH String value for it to work in macOS Sierra – ObjSal Jan 02 '17 at 21:10
  • @ObjSal Oh interesting! You had to prepend with that? On its own line or anything? – John Dorian Jan 03 '17 at 11:42
  • @JohnDorian I added the MX delay within the string in a new line after the Host IP address – ObjSal Jan 04 '17 at 03:41
  • For other service such as DIAL, you need to do `String MSEARCH = "M-SEARCH * HTTP/1.1\nMX: 5\nHost: 239.255.255.250:1900\nMan: \"ssdp:discover\"\nST: urn:dial-multiscreen-org:service:dial:1\n\n";`, which `MX: n` must appear at least after`HTTP/1.1`, and trailing extra `\n` is required. Change `\n` to `\r\n` is optional. – 林果皞 Apr 10 '17 at 12:06
  • I got Below Error java.io.IOException: sendto failed: EPERM (Operation not permitted) at libcore.io.IoBridge.maybeThrowAfterSendto(IoBridge.java:603) at libcore.io.IoBridge.sendto(IoBridge.java:571) at java.net.PlainDatagramSocketImpl.send(PlainDatagramSocketImpl.java:124) at java.net.DatagramSocket.send(DatagramSocket.java:721) – Sonia John Kavery Jun 22 '20 at 10:25