诸如此类的难题浩瀚,但办理方法实在很大略,那便是利用软件或者自己手写一个,可以支持访问我的电脑上的微信支付接口,从而实现这一系列的大略单纯操作。目前海内这方面企业级的做事商有壳和神卓互联,我打仗过很多公司在用,壳的技能是PHTunnel ,神卓互联用的是Wangooe Tunnel技能,这里就先容神卓互联的,接下来就先容和剖析这款软件的用法和技能要点。如果没有打仗过这方面技能的同学可以看一下这个图:
首先用法很大略,便是在界面上创建一条映射规则,填写运用名称和要连接的内网运用主机地址和端口号,
填写自己要穿透的运用名称和端口号,如果须要获取原访问者IP最好是选择Web运用。提交提交就可以了。
例如我须要发布一个Tomcat运用,访问端口号是7070,那么运用名称填写tomcat,内网主机填写127.0.0.1,内网端口填7070点提交就可以。
首先新建一个web项目
新建login.jsp上岸文件,内容如下:
<%@ page language=34;java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>登录系统</title><style type="text/css">table td{font: 14px/1.5 'Microsoft YaHei',arial,tahoma,\5b8b\4f53,sans-serif;}</style></head><body><table><tr><td>用户名</td><td><input type="text"></td></tr><tr><td>密码</td><td><input type="text"></td></tr><tr><td> </td><td><input type="submit" value="登录"></td></tr></table></body></html>
先在本地运行,看项目是否可以正常运行:
本地运行没有问题,可以正常打开,接下来就试一下外网访问
打开神卓互联软件主界面,右键选择外网访问
如果须要绑定域名访问的话也很大略,这里不多说。
接下来就剖析是如何做到将要求转发到内网由于又返回给访问客户真个。
InetAddress
//获取本机的InetAddress实例InetAddress address =InetAddress.getLocalHost();address.getHostName();//获取打算机名address.getHostAddress();//获取IP地址byte[] bytes = address.getAddress();//获取字节数组形式的IP地址,以点分隔的四部分 //获取其他主机的InetAddress实例InetAddress address2 =InetAddress.getByName("其他主机名");InetAddress address3 =InetAddress.getByName("IP地址");
URL类
//创建一个URL的实例URL baidu =new URL("http://www.baidu.com");URL url =new URL(baidu,"/index.html?username=tom#test");//?表示参数,#表示锚点url.getProtocol();//获取协议url.getHost();//获取主机url.getPort();//如果没有指定端口号,根据协议不同利用默认端口。此时getPort()方法的返回值为 -1url.getPath();//获取文件路径url.getFile();//文件名,包括文件路径+参数url.getRef();//相对路径,便是锚点,即#号后面的内容url.getQuery();//查询字符串,即参数
以下便是P2P打洞核心代码(TCP)
假设现在有以下3台机器:
外网机器,IP:121.56.21.85 , 以下简称“主机A”
处在内网1下的机器,外网IP:106.116.5.45 ,内网IP:192.168.1.10, 以下简称“主机1”
处在内网2下的机器,外网IP:104.128.52.6 ,内网IP:192.168.0.11,以下简称“主机2”
很显然内网的两台机器不能直接连接,我们现在要实现的是借助外网机器,让两台内网机器进行tcp直连通讯。
实现过程如下:
1、主机A启动做事端程序,监听端口8888,接管TCP要求。
2、启动主机1的客户端程序,连接主机A的8888端口,建立TCP连接。
3、启动主机2的客户端程序,连接主机A的8888端口,建立TCP连接。
4、主机2发送一个命令见告主机A,我哀求与其他设备进行连接,要求帮忙进行穿透。
5、主机A吸收到主机2的命令之后,会返回主机1的外网地址和端口给主机2,同时把主机2的外网地址和端口发送给主机1。
6、主机1和主机2在收到主机A的信息之后,同时异步发起对对方的连接。
7、在与对方发起连接之后,监听本地与主机A连接的端口(也可以在发起连接之前),(由于不同的操作系统对tcp的实现不尽相同,有的操作系统会在连接发送之后,把对方的连接当作是回应,即发出SYN之后,把对方发来的SYN当作是本次SYN的ACK,这种情形就不须要监听也可建立连接,本文的代码所在测试环境就不须要监听,测试环境为:做事器centos 7.3, 内网1 win10,内网2 win10和centos7.2都测试过)。
8、主机1和主机2成功连上,可以关闭主机A的做事,主机1和主机2的连接依然会持续生效,不关闭就形成了一个3方直连的拓扑网状构造网络。
做事器端代码:
package org.inchain.p2p; import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;import java.util.ArrayList;import java.util.List; / 外网端做事,穿透中继 @author ln /public class Server { public static List<ServerThread> connections = new ArrayList<ServerThread>(); public static void main(String[] args) {try {// 1.创建一个做事器端Socket,即ServerSocket,指定绑定的端口,并监听此端口ServerSocket serverSocket = new ServerSocket(8888);Socket socket = null;// 记录客户真个数量int count = 0;System.out.println("做事器即将启动,等待客户真个连接");// 循环监听等待客户真个连接while (true) {// 调用accept()方法开始监听,等待客户真个连接socket = serverSocket.accept();// 创建一个新的线程ServerThread serverThread = new ServerThread(socket);// 启动线程serverThread.start(); connections.add(serverThread); count++;// 统计客户真个数量System.out.println("客户真个数量:" + count);}} catch (IOException e) {e.printStackTrace();}}}
package org.inchain.p2p; import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.PrintWriter;import java.net.InetAddress;import java.net.Socket; / 外网端做事多线程处理内网端连接 @author ln /public class ServerThread extends Thread {// 和本线程干系的Socketprivate Socket socket = null;private BufferedReader br = null;private PrintWriter pw = null; public ServerThread(Socket socket) throws IOException {this.socket = socket;this.br = new BufferedReader(new InputStreamReader(socket.getInputStream()));this.pw = new PrintWriter(socket.getOutputStream());} // 线程实行的操作,相应客户真个要求public void run() { InetAddress address = socket.getInetAddress();System.out.println("新连接,客户真个IP:" + address.getHostAddress() + " ,端口:" + socket.getPort()); try {pw.write("已有客户端列表:" + Server.connections + "\n"); // 获取输入流,并读取客户端信息String info = null;while ((info = br.readLine()) != null) {// 循环读取客户真个信息System.out.println("我是做事器,客户端说:" + info); if (info.startsWith("newConn_")) {//吸收到穿透,关照目标节点String[] infos = info.split("_");//目标节点的外网ip地址String ip = infos[1];//目标节点的外网端口String port = infos[2];System.out.println("打洞到 " + ip + ":" + port);for (ServerThread server : Server.connections) {if (server.socket.getInetAddress().getHostAddress().equals(ip)&& server.socket.getPort() == Integer.parseInt(port)) {//发送命令关照目标节点进行穿透连接server.pw.write("autoConn_" + socket.getInetAddress().getHostAddress() + "_" + socket.getPort()+ "\n");server.pw.flush();break;}}} else {// 获取输出流,相应客户真个要求pw.write("欢迎您!
" + info + "\n");// 调用flush()方法将缓冲输出pw.flush();}}} catch (Exception e) {e.printStackTrace();} finally {System.out.println("客户端关闭:" + address.getHostAddress() + " ,端口:" + socket.getPort());Server.connections.remove(this);// 关闭资源try {if (pw != null) {pw.close();}if (br != null) {br.close();}if (socket != null) {socket.close();}} catch (IOException e) {e.printStackTrace();}}} @Overridepublic String toString() {return "ServerThread [socket=" + socket + "]";}}
末了附上测试方法和运行效果:
利用方法:
1、在做事器启动Server。
2、在客户端1启动Client,输入notwait命令,等待做事器关照打洞。
3、在客户端2启动Client,输入conn命令,然后输入做事器返回的客户端1的外网ip和端口,接下来就会自动完成连接。
运行效果:
客户端1运行结果 (穿透成功之后,客户端会把穿透对方返回的内容发送给做事器,做事器再返回)
客户端1利用netstat查看的网络连接
客户端2的运行结果
客户端2利用netstat查看的网络连接
可以看到客户端2对应的端口不同,那是由于电信NAT的问题,本地获取的Ip是电信10开头的内网地址,相称于在客户端2的上层还进行了一次中继。
s:由于没有对称型的NAT设备,无法做深入研究,对称型设备的端口太难预测,穿透成功概率很小。