Comprendre du point de vue d'une boîte noire : Généralement, qu'un ordinateur personnel soit connecté au WIFI ou à un câble réseau, il appartient au réseau local. La pénétration du réseau permet aux ordinateurs de votre réseau local d'accéder au réseau externe. Un exemple : Si vous exécutez un service Web localement et que le port occupé est 8080, alors votre test local est : //localhost:8080. Mais que se passe-t-il si vous souhaitez partager vos services avec un bon ami ? Oui, c'est grâce à la pénétration de l'intranet. En fait, la pénétration intranet est une opération très compliquée. L'explication sur l'Encyclopédie Baidu est la suivante :
La pénétration intranet, c'est-à-dire la pénétration NAT, est effectuée pour permettre aux utilisateurs disposant d'une adresse IP source spécifique et aux paquets de données avec la source. Les numéros de port ne sont pas bloqués par le périphérique NAT et sont correctement acheminés vers l'hôte intranet.
Je ne peux évidemment pas le faire ici. Tout ce dont j'ai besoin, c'est du service pour accéder à l'intranet depuis le réseau externe. Quant au processus spécifique, je m'en fiche, j'ai juste besoin d'atteindre cet objectif.
Quelle que soit la méthode utilisée pour parvenir à la pénétration de l'intranet, j'utilise ici un serveur Alibaba Cloud. Ce qui suit est un diagramme schématique de l'ensemble de la simulation :
Remarque :
1 Le serveur de pénétration intranet est déployé sur une machine avec une IP publique.
2. Les services intranet et les clients de pénétration intranet sont déployés sur les machines intranet.
Explication :
Mon idée est très simple, c'est-à-dire que l'utilisateur accède au serveur de pénétration intranet, puis le serveur de pénétration intranet transmet le message de demande de l'utilisateur au client de pénétration intranet, puis à l'intranet serveur de pénétration Le client de pénétration du réseau transmet le message de demande au service intranet, puis reçoit le message de réponse du service intranet, le transmet au serveur de pénétration intranet, et enfin le serveur de pénétration intranet l'envoie Transféré à l'utilisateur. Le processus général est le suivant : pour les utilisateurs externes, il pensera seulement qu'ils ont accédé à un service réseau externe, car l'utilisateur est confronté à un système de boîte noire.
Afin d'atteindre l'objectif ci-dessus, la chose la plus critique est de maintenir une longue connexion entre le client de pénétration intranet et le serveur de pénétration intranet. Je dois utiliser cette longue connexion pour échanger des rapports des deux. parties. Par conséquent, cette longue connexion doit être établie après le démarrage du système. Lorsqu'une demande d'utilisateur arrive, le serveur de pénétration intranet reçoit d'abord la demande, puis utilise la longue connexion pour la transférer au client de pénétration intranet utilisé. ce message sous la forme d'une demande d'accès au service intranet, puis reçoit la réponse du service intranet, la transmet au serveur de pénétration intranet et enfin la transmet à l'utilisateur.
3. Implémentation du code3.1 Structure du répertoire Explication : Il s'agit du code serveur et client pour la pénétration intranet. Je ne les ai pas écrits séparément car les deux parties doivent utiliser une classe commune. Il est toutefois recommandé de le séparer en deux projets car ils doivent être déployés séparément.Ou lors de l'exportation dans un package jar, sélectionnez simplement différentes classes principales.
Fichiers de code client : Client.java, Connection.java, Msg.java, ProxyConnection.java.Fichiers de code côté serveur : 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; } }
Démarrez le serveur de pénétration du réseau interne et le client de pénétration du réseau interne, puis accédez aux trois URL dans le navigateur. Remarque : 1. Si vous le testez vous-même, vous pouvez basculer vers l'adresse IP du serveur de pénétration intranet que vous exécutez ou utiliser un nom de domaine. 2. La machine réseau externe et la machine réseau interne utilisent ici des ports différents (utilisez-les avec désinvolture, tant qu'ils n'entrent pas en conflit avec le port de service de votre propre machine. En fait, vous pouvez utiliser le port 80 sur le réseau externe). ce qui est plus convivial pour les utilisateurs ordinaires. 3. Le troisième test a échoué. Vous pouvez voir l'animation de chargement ci-dessus, qui continue de se charger. Il va de soi que cela devrait bientôt s’arrêter, mais cela semble impossible à arrêter. Il s’agit d’un bug du système, mais en raison de mes connaissances limitées, je ne le résoudrai pas.
Le code ici est une simulation, il ne peut que simuler cette fonction, mais il n'a fondamentalement aucun effet réel, haha. Parce que je n'ai qu'une seule longue connexion ici, je ne peux prendre en charge que la communication série. Il est préférable de simplement l'appeler par une seule personne. Il semble que la vitesse d'appel ne puisse pas être trop rapide. J'ai pensé à un moyen de maintenir un pool de connexions entre le client et le serveur, afin de permettre un accès multithread. Il n'y a pas de gestion du collage et du sous-packaging des paquets TCP ici (je comprends ce concept, mais je ne suis pas très doué pour le gérer), donc par défaut, les messages de demande et de réponse ont une taille inférieure à 2 Ko. Dépasser cette longueur entraînera des problèmes. Bien que ce paramètre puisse être augmenté, si la plupart des paquets sont très petits, cela entraînera également une faible efficacité. Cette pénétration intranet peut prendre en charge divers protocoles supérieurs à TCP, pas nécessairement HTTP, du moins en théorie, c'est possible.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!