java - 通过使用 UPnP 发送广播数据包来发现路由器不起作用

标签 java broadcast upnp datagram

我正在尝试实现一个简单的库,如果应用程序在 NAT 环境中运行,它可以通过 UPnP 协议(protocol)发现路由器。我尝试了两种方式,多播和数据报,将发现数据包发送到路由器,并尝试监听端口 1901 以获取路由器的响应。但是,我在代码方面遇到了一些问题。我尝试了以下三种方式,只有第三种方式能正确接收到路由器的响应。我不知道为什么它在第一种和第二种方式上不起作用。

第一种:发送多播包,监听1901端口响应。

代码:

public void discovery () throws IOException {
    // SSDP port
    final int SSDP_PORT = 1900;
    final int SSDP_SEARCH_PORT = 1901;
    // Broadcast address for finding routers.
    final String SSDP_IP = "239.255.255.250";
    // Time out of the connection.
    int TIMEOUT = 5000;
    // Localhost address.
    InetAddress localhost = InetAddress.getLocalHost();

    // Send from localhost:1901
    InetSocketAddress srcAddress = new InetSocketAddress(localhost, SSDP_SEARCH_PORT);
    // Send to 239.255.255.250:1900
    InetSocketAddress dstAddress = new InetSocketAddress(InetAddress.getByName(SSDP_IP), SSDP_PORT);
    
    // ----------------------------------------- //
    //       Construct the request packet.       //
    // ----------------------------------------- //
    StringBuffer discoveryMessage = new StringBuffer();
    discoveryMessage.append("M-SEARCH * HTTP/1.1\r\n");
    discoveryMessage.append("HOST: " + SSDP_IP + ":" + SSDP_PORT + "\r\n");
    discoveryMessage.append("ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n");
    // ST: urn:schemas-upnp-org:service:WANIPConnection:1\r\n
    discoveryMessage.append("MAN: \"ssdp:discover\"\r\n");
    discoveryMessage.append("MX: 2\r\n");
    discoveryMessage.append("\r\n");
    // System.out.println("Request: " + discoveryMessage.toString() + "\n");
    byte[] discoveryMessageBytes = discoveryMessage.toString().getBytes();
    DatagramPacket discoveryPacket = new DatagramPacket(discoveryMessageBytes, discoveryMessageBytes.length, dstAddress);
    
    // ----------------------------------- //
    //       Send multi-cast packet.       //
    // ----------------------------------- //
    MulticastSocket multicast = null;
    try {
        multicast = new MulticastSocket(null);
        multicast.bind(srcAddress);
        multicast.setTimeToLive(4);
        System.out.println("Send multicast request.");
        // ----- Sending multi-cast packet ----- //
        multicast.send(discoveryPacket);
    } finally {
        System.out.println("Multicast ends. Close connection.");
        multicast.disconnect();
        multicast.close();
    }

    // -------------------------------------------------- //
    //       Listening to response from the router.       //
    // -------------------------------------------------- //
    DatagramSocket wildSocket = null;
    DatagramPacket receivePacket = null;
    try {
        wildSocket = new DatagramSocket(SSDP_SEARCH_PORT);
        wildSocket.setSoTimeout(TIMEOUT);

        while (true) {
            try {
                System.out.println("Receive ssdp.");
                receivePacket = new DatagramPacket(new byte[1536], 1536);
                wildSocket.receive(receivePacket);
                String message = new String(receivePacket.getData());
                System.out.println("Recieved messages:");
                System.out.println(message);
            } catch (SocketTimeoutException e) {
                System.err.print("Time out.");
                break;
            }
        }
    } finally {
        if (wildSocket != null) {
            wildSocket.disconnect();
            wildSocket.close();
        }
    }
}

结果:路由器有响应包(被Wireshark嗅探,如下截图),但是代码没有收到任何东西。 code 1

代码结果:

Send multicast request.
Multicast ends. Close connection.
Receive ssdp.
Time out.

第二种:发送数据报包,监听1901端口响应。

代码:

public void discovery () throws IOException {
    // Ignore this part of the codes since they are the same as the first one.
    ..............

    // -------------------------------------------------- //
    //       Listening to response from the router.       //
    // -------------------------------------------------- //
    DatagramSocket wildSocket = null;
    DatagramPacket receivePacket = null;
    try {
        wildSocket = new DatagramSocket(SSDP_SEARCH_PORT);
        wildSocket.setSoTimeout(TIMEOUT);
        // ----- Sending datagram packet ----- //
        System.out.println("Send datagram packet.");
        wildSocket.send(discoveryPacket);

        while (true) {
            try {
                System.out.println("Receive ssdp.");
                receivePacket = new DatagramPacket(new byte[1536], 1536);
                wildSocket.receive(receivePacket);
                String message = new String(receivePacket.getData());
                System.out.println("Recieved messages:");
                System.out.println(message);
            } catch (SocketTimeoutException e) {
                System.err.print("Time out.");
                break;
            }
        }
    } finally {
        if (wildSocket != null) {
            wildSocket.disconnect();
            wildSocket.close();
        }
    }
}

结果:Wireshark 没有得到任何东西。 1900和1901端口没有嗅探到数据包。

代码结果:

Send datagram packet.
Receive ssdp.
Time out.

第三种:发送多播和数据报包,监听1901端口响应。

代码:

public void discovery () throws IOException {
    // Ignore this part of the codes since they are the same as the first one.
    ..............

    // ----------------------------------- //
    //       Send multi-cast packet.       //
    // ----------------------------------- //
    MulticastSocket multicast = null;
    try {
        multicast = new MulticastSocket(null);
        multicast.bind(srcAddress);
        multicast.setTimeToLive(4);
        System.out.println("Send multicast request.");
        // ----- Sending multi-cast packet ----- //
        multicast.send(discoveryPacket);
    } finally {
        System.out.println("Multicast ends. Close connection.");
        multicast.disconnect();
        multicast.close();
    }

    // -------------------------------------------------- //
    //       Listening to response from the router.       //
    // -------------------------------------------------- //
    DatagramSocket wildSocket = null;
    DatagramPacket receivePacket = null;
    try {
        wildSocket = new DatagramSocket(SSDP_SEARCH_PORT);
        wildSocket.setSoTimeout(TIMEOUT);
        // ----- Sending datagram packet ----- //
        System.out.println("Send datagram packet.");
        wildSocket.send(discoveryPacket);

        while (true) {
            try {
                System.out.println("Receive ssdp.");
                receivePacket = new DatagramPacket(new byte[1536], 1536);
                wildSocket.receive(receivePacket);
                String message = new String(receivePacket.getData());
                System.out.println("Recieved messages:");
                System.out.println(message);
            } catch (SocketTimeoutException e) {
                System.err.print("Time out.");
                break;
            }
        }
    } finally {
        if (wildSocket != null) {
            wildSocket.disconnect();
            wildSocket.close();
        }
    }
}

结果:发送两个广播包成功,并得到路由器的两个响应。 code 1

代码结果:

Send multicast request.
Multicast ends. Close connection.
Send datagram packet.
Receive ssdp.
Recieved messages:
HTTP/1.1 200 OK
Cache-Control: max-age=300
Date: Wed, 06 Mar 2013 05:15:43 GMT
Ext: 
Location: http://192.168.1.1:1780/InternetGatewayDevice.xml
Server: POSIX UPnP/1.0 DD-WRT Linux/V24
ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1
USN: uuid:C42C1F3F-6E63-7FFC-F982-035B355D6E76::urn:schemas-upnp-org:device:InternetGatewayDevice:1

Receive ssdp.
Recieved messages:
HTTP/1.1 200 OK
Cache-Control: max-age=300
Date: Wed, 06 Mar 2013 05:15:43 GMT
Ext: 
Location: http://192.168.1.1:1780/InternetGatewayDevice.xml
Server: POSIX UPnP/1.0 DD-WRT Linux/V24
ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1
USN: uuid:C42C1F3F-6E63-7FFC-F982-035B355D6E76::urn:schemas-upnp-org:device:InternetGatewayDevice:1

Receive ssdp.
Time out.

是否知道为什么第一种和第二种方式无法通过 UPnP 协议(protocol)请求路由器?为什么第二个似乎没有发送任何东西?

非常感谢!

最佳答案

我猜这可能是操作系统+路由器固件相关的问题。 确保您的防火墙已关闭。

第一种方法: 此方法在我的计算机 (OS X) 上根本不起作用:

Send multicast request.
Exception in thread "main" java.io.IOException: Can't assign requested address
Multicast ends. Close connection.
    at java.net.PlainDatagramSocketImpl.send(Native Method)
    at java.net.DatagramSocket.send(DatagramSocket.java:676)
    at Test.discovery(Test.java:55)
    at Test.main(Test.java:93)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)

但是,在我改变之后:

    InetSocketAddress srcAddress = new InetSocketAddress(localhost, SSDP_SEARCH_PORT);

    InetSocketAddress srcAddress = new InetSocketAddress(SSDP_SEARCH_PORT);

它运行良好:

Send multicast request.
Multicast ends. Close connection.
Receive ssdp.
Recieved messages:
HTTP/1.1 200 OK
Cache-Control: max-age=60
Date: Sun, 04 Jan 1970 21:55:18 GMT
Ext: 
Location: http://192.168.0.2:1780/InternetGatewayDevice.xml
Server: POSIX UPnP/1.0 linux/5.20.61.0
ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1
USN: uuid:C04E066F-F351-72B6-CCCF-E98237DCB05C::urn:schemas-upnp-org:device:InternetGatewayDevice:1


Receive ssdp.
Time out.

第二种方法: 有效。

第三种方法: 有效。

(评论太长)

[编辑]

如果你的目标是生产稳定的软件,我会建议坚持传统的方式: https://stackoverflow.com/a/4405143

关于java - 通过使用 UPnP 发送广播数据包来发现路由器不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15239011/

相关文章:

Java lwjgl 小程序 noclassdeffound

c++ - 接收客户端通过互联网发送的数据报

xml - UPnP 和 SOAP 到路由器

java - UPnP Java 库的问题

Java 访问数据库连接

OSX 上的 Java 声音不支持 Port.Info 类型

java - 如何在C、java和C++中编译和运行任意位置的任意文件?

Delphi TCP Socket Server - 一次广播/回复多个客户端

flash - 流媒体视频,如 youtube、vimeo 等?我的自由和开源软件选项有哪些?

sockets - 监听对 UDP 多播消息的 UDP 单播回复