通过使用 UPnP 发送广播数据包来发现路由器不起作用
Posted
技术标签:
【中文标题】通过使用 UPnP 发送广播数据包来发现路由器不起作用【英文标题】:Discovering the router by sending broadcast packet using UPnP doesn't work 【发布时间】:2013-02-20 18:02:44 【问题描述】:我正在尝试实现一个简单的库,如果应用程序在 NAT 环境中运行,它可以通过 UPnP 协议发现路由器。我尝试了两种方法,多播和数据报,将发现数据包发送到路由器,并尝试监听端口 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 嗅探,如下图所示),但代码没有收到任何内容。
代码结果:
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();
结果:发送两个广播包成功,并从路由器得到两个响应。
代码结果:
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 协议请求路由器吗?为什么第二个似乎没有发送任何内容?
非常感谢!
【问题讨论】:
+1 很好地展示了这个问题。 【参考方案1】:我猜这可能是操作系统+路由器固件相关的问题。 确保您的防火墙已关闭。
第一个方法: 这种方法在我的电脑(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://***.com/a/4405143
【讨论】:
以上是关于通过使用 UPnP 发送广播数据包来发现路由器不起作用的主要内容,如果未能解决你的问题,请参考以下文章