ブラック ボックスの観点から理解する: 通常、パーソナル コンピューターがインターネットにアクセスするために WIFI に接続されているか、ネットワーク ケーブルを使用してインターネットにアクセスしているか。インターネットにアクセスする場合、インターネットは LAN の内部に属します。インターネットはコンピュータに直接アクセスできません。イントラネットの侵入により、LAN 内のコンピュータが外部ネットワークにアクセスできるようになります。 例を示します: Web サービスをローカルで実行し、占有されているポートが 8080 の場合、ローカル テストは //localhost:8080 になります。しかし、自分のサービスを仲の良い友人と共有したい場合はどうすればよいでしょうか?はい、それはイントラネットの侵入によるものです。実際、イントラネット ペネトレーションは非常に複雑な操作です。Baidu Encyclopedia の説明は次のとおりです。
イントラネット ペネトレーション、つまり NAT ペネトレーション。NAT ペネトレーションは、特定のソースを持つ特定の A データ パケットを有効にするために実行されます。 IP アドレスと送信元ポート番号は NAT デバイスによってブロックされず、イントラネット ホストに正しくルーティングされます。
ここでは明らかにできません。必要なのは、外部ネットワークからイントラネットにアクセスするためのサービスだけであり、具体的なプロセスについては、この目的を達成するだけで構いません。
イントラネットへの侵入を達成するためにどの方法を使用する場合でも、パブリック IP アドレスが必要です。 Alibaba Cloudサーバーを使用します。以下は、シミュレーション全体の概略図です:
注:
1. イントラネット ペネトレーション サーバーは、パブリック IP を持つマシンに展開されます。
2. イントラネット サービスとイントラネット ペネトレーション クライアントは、イントラネット マシンに展開されます。
説明:
私の考えは非常に単純です。つまり、ユーザーが イントラネット ペネトレーション サーバー にアクセスし、イントラネット ペネトレーション サーバーがユーザーの リクエストを送信します。メッセージ イントラネット ペネトレーション クライアント に転送すると、イントラネット ペネトレーション クライアントは要求メッセージを イントラネット サービス に転送し、イントラネット サービスの 応答メッセージを受信します はイントラネット ペネトレーション サーバーに転送され、最後にイントラネット ペネトレーション サーバーはそれをユーザーに転送します。 一般的なプロセスは次のとおりです。外部ユーザーはブラック ボックス システムに直面しているため、外部ユーザーは外部ネットワーク サービスにアクセスしたとしか認識しません。
上記の目標を達成するには、イントラネット ペネトレーション クライアントとイントラネット ペネトレーション サーバー間の接続を長時間維持することが最も重要です、双方からのメッセージ情報を交換するには、この長い接続を使用する必要があります。したがって、システム起動後にこの長い接続を確立する必要があり、ユーザーからの要求が受信されると、まずイントラネットペネトレーションサーバーがその要求を受信し、その後長い接続を使用してその要求をイントラネットペネトレーションクライアントに転送します。は、このメッセージをイントラネット サービスへのアクセス要求として使用し、イントラネット サービスから応答を受信し、それをイントラネット ペネトレーション サーバーに転送し、最後にユーザーに転送します。
3. コードの実装3.1 ディレクトリ構造 説明: これは、イントラネット侵入用のサーバーとクライアントのコードです。両方で共通のクラスを使用する必要があるため、別々に記述するのではなく、一緒に記述します。ただし、別々にデプロイする必要があるため、2 つのプロジェクトに分けることをお勧めします。または、jar パッケージにエクスポートする場合は、別のメイン クラスを選択するだけです。
クライアント コード ファイル: Client.java、Connection.java、Msg.java、ProxyConnection.java。サーバー コード ファイル: Server.java、Connection.java、Msg.java、ProxyConnection.java。
package org.dragon; import java.io.IOException; import java.net.Socket; import java.net.UnknownHostException; /** * 用于双向通信的客户端 * */ public class Client { private static final String REMOTE_HOST = "公网IP"; private static final String LOCAL_HOST = "127.0.0.1"; public static void main(String[] args) { try { Socket proxy = new Socket(REMOTE_HOST, 10000); System.out.println("Connect Server Successfully!"); ProxyConnection proxyConnection = new ProxyConnection(proxy); // 维持和内网穿透服务端的长连接 // 可以实现同一个人多次访问 while (true) { Msg msg = proxyConnection.receiveMsg(); Connection connection = new Connection(new Socket(LOCAL_HOST, 8080)); connection.sendMsg(msg); // 将请求报文发送给内网服务器,即模拟发送请求报文 msg = connection.receiveMsg(); // 接收内网服务器的响应报文 proxyConnection.sendMsg(msg); // 将内网服务器的响应报文转发给公网服务器(内网穿透服务端) } } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
package org.dragon; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; /** * 维持用户和服务器的连接 * */ public class Connection { private InputStream input; private OutputStream output; public Connection(Socket client) throws IOException { this.input = new BufferedInputStream(client.getInputStream()); this.output = new BufferedOutputStream(client.getOutputStream()); } public Msg receiveMsg() throws IOException { byte[] msg = new byte[2*1024]; int len = input.read(msg); return new Msg(len, msg); } public void sendMsg(Msg msg) throws IOException { output.write(msg.getMsg(), 0, msg.getLen()); output.flush(); // 每一次写入都要刷新,防止阻塞。 } }
package org.dragon; public class Msg { private int len; private byte[] msg; public Msg(int len, byte[] msg) { this.len = len; this.msg = msg; } public int getLen() { return len; } public byte[] getMsg() { return msg; } @Override public String toString() { return "msg: " + len + " --> " + new String(msg, 0, len); } }
package org.dragon; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; import java.net.UnknownHostException; /** * @author Alfred * * 代理服务器和代理客户端是用于维持两者之间通信的一个长连接Socket, * 主要的目的是因为双方之间的通信方式是全双工的,它们的作用是为了传递报文。 * */ public class ProxyConnection { private Socket proxySocket; private DataInputStream input; private DataOutputStream output; public ProxyConnection(final Socket socket) throws UnknownHostException, IOException { proxySocket = socket; input = new DataInputStream(new BufferedInputStream(proxySocket.getInputStream())); output = new DataOutputStream(new BufferedOutputStream(proxySocket.getOutputStream())); } /** * 接收报文 * @throws IOException * */ public Msg receiveMsg() throws IOException { int len = input.readInt(); if (len <= 0) { throw new IOException("异常接收数据,长度为:" + len); } byte[] msg = new byte[len]; int size = input.read(msg); // 这里到底会不会读取到这么多,我也有点迷惑! return new Msg(size, msg); // 为了防止出错,还是使用一个记录实际读取值size } /** * 转发报文 * @throws IOException * */ public void sendMsg(Msg msg) throws IOException { output.writeInt(msg.getLen()); output.write(msg.getMsg(), 0, msg.getLen()); output.flush(); // 每一次写入都需要手动刷新,防止阻塞。 } }
package org.dragon; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; /** * 用于双向通信的服务器 * */ public class Server { public static void main(String[] args) { try (ServerSocket server = new ServerSocket(10000)) { // 用于交换控制信息的Socket Socket proxy = server.accept(); ProxyConnection proxySocket = new ProxyConnection(proxy); // 用于正常通讯的socket while (true) { Socket client = server.accept(); Connection connection = new Connection(client); Msg msg = connection.receiveMsg(); // 接收用户的请求报文 proxySocket.sendMsg(msg); // 转发用户的请求报文给内网服务器 msg = proxySocket.receiveMsg(); // 接收内网服务器的响应报文 connection.sendMsg(msg); // 转发内网服务器的响应报文给用户 } } catch (IOException e) { e.printStackTrace(); } } }
package org.dragon.controller; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class Controller { @GetMapping("/loveEN") public String testEN() { return "I love you yesterday and today!"; } @GetMapping("/loveZH") public String loveZH() { return "有一美人兮,见之不忘。一日不见兮,思之如狂。凤飞翱翔兮,四海求凰。无奈佳人兮,不在东墙。"; } @GetMapping("/loveJson") public Map<String, String> loveJson() { HashMap<String, String> map = new LinkedHashMap<>(); map.put("english", "I love you yesterday and today!"); map.put("chinese", "有一美人兮,见之不忘。一日不见兮,思之如狂。" + "凤飞翱翔兮,四海求凰。无奈佳人兮,不在东墙。"); return map; } }
イントラネットペネトレーションサーバー、イントラネットペネトレーションクライアントを順に起動し、ブラウザで 3 つの URL にアクセスします。注: 1. 自分でテストする場合は、実行しているイントラネット ペネトレーション サーバーの IP アドレスに切り替えるか、ドメイン名を使用できます。 2. ここで、外部ネットワーク マシンと内部ネットワーク マシンは異なるポートを使用します (自分のマシンのサービス ポートと競合しない限り、気軽に使用してください)。実際には、外部ネットワークのポート 80 を使用できます。これは一般ユーザーにとってよりフレンドリーです。 3. 3 番目のテストは実際に失敗しました。上記の読み込みアニメーションが読み込みを続けていることがわかります。これをすぐに止めるのは当然ですが、止めるのは不可能のようです。 これはシステムのバグですが、私の知識が少ないため解決しません。
ここでのコードは A です。シミュレーションでは、この機能をシミュレートすることしかできませんが、基本的には実際の効果はありません(笑)。ここでは長い接続が 1 つしかないため、シリアル通信しかサポートできません。単純に 1 人で呼び出すのが最適です。通話速度はあまり速くてもいけないようです。 マルチスレッドアクセスを実現できるように、クライアントとサーバー間の接続プールを維持する方法を考えました。 ここでは、TCP パケットのスティッキングとサブパッケージ化の処理はありません (この概念は理解していますが、処理があまり得意ではありません)。そのため、デフォルトで要求メッセージと応答メッセージのサイズを 2KB 以内にします。この長さを超えると問題が発生するため、このパラメータを増やすことはできますが、ほとんどのパケットが非常に小さい場合は、効率の低下にもつながります。このイントラネットの浸透は、必ずしも HTTP ではなく、TCP 上のさまざまなプロトコルをサポートできますが、少なくとも理論的には可能です。
以上がJava シミュレーションを使用してイントラネットのブラック ボックスへの侵入を実現するにはどうすればよいですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。