Search code examples
javaupnp

UPnP events not received or received late


I've been working on a simple UPnP client implementation but I'm having trouble with receiving events. To try and find faults in the messages I'm sending, I tested my use case with CLing (which is an awesome library but it depends on Java 1.5 while I only have access to 1.4) which works perfectly, but I can't find any meaningful difference between its messages and mine.

I'm subscribing to /MediaRenderer/AVTransport/Event events, testing to see if I can get the latest TransportState.

Here's my test code:

private static final String PLAYER_IP = "192.168.10.101";

public static void main(String[] args) throws Exception {
    ServerSocket serverSocket = new ServerSocket();
    serverSocket.bind(null);

    System.out.println("Started server on port " + serverSocket.getLocalPort() + ", ready for connections");

    sendSubscription(serverSocket.getLocalPort());

    while (!serverSocket.isClosed())
        handle(serverSocket.accept());
}

private static void sendSubscription(int port) throws Exception {
    Socket socket = new Socket(PLAYER_IP, 1400);
    PrintWriter out = new PrintWriter(socket.getOutputStream());

    out.print("SUBSCRIBE /MediaRenderer/AVTransport/Event HTTP/1.1\r\n"
            + "HOST: " + PLAYER_IP + ":1400\r\n"
            + "USER-AGENT: MacOSX/10.12.4 UPnP/1.0 MyApp/0.1\r\n"
            + "CALLBACK: <http://" + InetAddress.getLocalHost().getHostAddress() + ":" + port + "/dev>\r\n"
            + "NT: upnp:event\r\n"
            + "TIMEOUT: Second-3600\r\n"
            + "\r\n");
    out.flush();

    BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    String line;
    while ((line = in.readLine()) != null)
        System.out.println(line);
}

private static void handle(final Socket clientSocket) {
    new Thread(new Runnable() {
        public void run() {
            System.out.println();
            System.out.println("Request incoming " + new Date());
            try {
                BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                String line;
                while ((line = in.readLine()) != null) {
                    System.out.println(line);
                    if (line.contains("TransportState"))
                        System.out.println("TransportState: " + line.substring(line.indexOf("TransportState") + 25));
                }
            }
            catch (IOException e) {}

            try {
                PrintWriter out = new PrintWriter(clientSocket.getOutputStream());
                out.print("HTTP/1.1 200 OK\r\n"
                        + "Server: MacOSX/10.12.4 UPnP/1.0 MyApp/0.1\r\n"
                        + "Connection: close\r\n"
                        + "\r\n");
                out.flush();
            }
            catch (IOException e) {}
        }
    }).start();
}

It sends a subscription request and then continuously reads notifications. You might notice that I'm not using any libraries for anything, but I've eliminated them because of paranoia until I can get this basic use-case to work.

The result is that, when I use my phone for instance to play/stop a track or radio station, the events don't always arrive and when they do, they are often very late. This behaviour is consistent for my app and not for CLing, so I concluded that I can rule out my network. I've Wiresharked the difference between my app and CLing and the subscription messages are almost identical (I've even tried the exact message CLing sends). Both applications are running on the same PC, both on the same JVM.

As per UPnP specification, the media renderer sends a notification immediately after subscribing, which always does arrive and I don't see any unexpected delay there.

This is very strange to me and I'm at a loss of what I can try next. I would be eternally grateful to any one who has an idea of what the cause of this issue could be!


Solution

  • The problem was with a HTTP server implementation who didn't send HTTP response codes in some cases. The media renderer wouldn't send the next event before it received a HTTP 200 response. Seems a good, lightweight HTTP server for Java 1.4 is a hard find.