多年前曾经写过一个关于NAT钻洞的实验。现在发现那个做法在我现在的路由器上已经不管用了。经过一番搜索发现时过境迁,世界变化很快,新路由器已经是UPnP了。在这里重新理一下几种方法。

第一种,也是不太靠谱的一种,因为没有特定的标准。这种方法依靠路由器的特定逻辑:

– 路由器尽可能保持内部端口和外部端口一致。所以你可以假设自己的内部端口就是外部端口。或者路由器尽量使用同一外部端口对应某一内部端口。
– 在内部应用发出UDP消息后,路由器允许任何外部设备通过上述外部端口发送消息到同一内部端口。
– 外部IP的发现可以使用其它方法,比如通过发送消息给外部服务器端,服务器端可以发回外部IP和端口信息。

这用方法现在应该不是用的很多了,或许有些路由器支持,甚至只支持这种。这用方法可能有较大安全漏洞。

第二种,依赖NAT-PMP(NAT Port Mapping Protocol)或者它的后续协议PCP(Port Control Protocol). 这两个协议要求应用发送特定二进制格式的UDP报文给网关。经过测试,我的路由器不支持此两种协议。

http://en.wikipedia.org/wiki/Port_Control_Protocol

http://en.wikipedia.org/wiki/NAT_Port_Mapping_Protocol

第三种,使用UPnP协议。这个是我的路由器支持的。估计很多路由器都支持此方法。此方法基于HTTP协议。并通过UDP Multicast(多播)来广播和获取网络上的服务。此后可以使用正常的基于TCP的HTTP来做一种SOAP方式的服务调用。

关于通信过程,这里有一篇很详细的介绍:http://blog.csdn.net/ydfok/article/details/1516040

下面贴一段简单的示例代码,完成第一步 – 发现网关的操作:

import java.io.IOException;

import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;

public class Upnp {
public static void main(String… args) throws IOException {
InetAddress group = InetAddress.getByName(“239.255.255.250″);
int port = 1900;
MulticastSocket socket = new MulticastSocket(1900);
socket.joinGroup(group);

String seekInternetGateway = “M-SEARCH * HTTP/1.1\r\n” + //
“Host:239.255.255.250:1900\r\n” + //
“ST:urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n” + //
“Man:\”ssdp:discover\”\r\n” + //
“MX:3\r\n”;//
byte[] requestData = seekInternetGateway.getBytes();
DatagramPacket request = new DatagramPacket(requestData,
requestData.length, group, port);
socket.setLoopbackMode(false);
System.out.println(socket.getLoopbackMode());
socket.send(request);

DatagramPacket response = new DatagramPacket(new byte[1024], 1024);
// will receive the message we sent and the response from gateway
for (int i = 0; i < 2; i++) {
socket.receive(response);
String responseText = new String(response.getData(),
response.getOffset(), response.getLength());
System.out.println(responseText);
}
socket.leaveGroup(group);
socket.close();
}
}

最新文章

  1. Vue.js起手式+Vue小作品实战
  2. python 基于windows环境的ftp功能
  3. Java中字符串比较时==和equals的区别
  4. changepassword.c 0.9:一个通过WEB界面更改LINUX用户密码的程序
  5. Android开发框架
  6. Laravel 实现 Facades 功能
  7. web.xml中servlet, bean, filter, listenr 加载顺序汇总
  8. 智力大冲浪(riddle)
  9. 开涛spring3(9.2) - Spring的事务 之 9.2 数据库事务概述
  10. 【批处理学习笔记】第十一课:常用DOS命令(1)
  11. java封装的概念
  12. Java 平时作业四
  13. 在ubuntu服务器上安装tomcat 9
  14. 【优秀的艺术文字和图标设计软件】Art Text 3.2.3 for Mac
  15. YUV的数据格式
  16. js中函数对象创建的总结
  17. 关于Unity中摇杆的操作
  18. hdu3488 Tour 拆点+二分图最佳匹配
  19. git 对比两个分支差异
  20. FoxMail提示:请求的名称有效,但是找不到请求的类型的数据

热门文章

  1. ajax传递的数据类型json传递
  2. csv格式导出文件
  3. 锅巴H264播放器地址和说明
  4. 关于oracle数据库(2)
  5. 41个有关Python的小技巧【转】
  6. events.js:72 throw er; // Unhandled &#39;error&#39; event
  7. mac中eclipse安装openExplore插件
  8. MySQL复制表结构,表数据。
  9. linux脚本Shell之awk详解(二)
  10. WHM使用手册by lin