블랙박스 관점에서 이해하기: 일반적으로 개인용 컴퓨터는 WIFI에 연결되어 있든, 네트워크 케이블로 연결되어 있든, 외부 네트워크에서는 직접 접속할 수 없습니다. 네트워크 침투를 통해 LAN에 있는 컴퓨터가 외부 네트워크에 액세스할 수 있습니다. 예: 웹 서비스를 로컬에서 실행하고 점유된 포트가 8080인 경우 로컬 테스트는 //localhost:8080입니다. 하지만 좋은 친구와 서비스를 공유하고 싶다면 어떻게 해야 할까요? 예, 인트라넷 침투를 통해서입니다. 실제로 인트라넷 침투는 매우 복잡한 작업입니다. Baidu Encyclopedia에 대한 설명은 다음과 같습니다.
인트라넷 침투, 즉 NAT 침투는 특정 소스 IP 주소를 가진 사용자와 소스가 있는 데이터 패킷을 활성화하기 위해 수행됩니다. 포트 번호는 NAT 장치에 의해 차단되지 않으며 인트라넷 호스트로 올바르게 라우팅됩니다.
분명히 여기서는 할 수 없습니다. 나에게 필요한 것은 외부 네트워크에서 인트라넷에 접속하는 서비스뿐이다. 구체적인 프로세스는 상관없다. 단지 이 목표를 달성하기만 하면 된다.
인트라넷 침투를 위해 어떤 방법을 사용하든 여기서는 알리바바 클라우드 서버를 사용하고 있습니다. 다음은 전체 시뮬레이션의 개략도입니다.
참고:
1 인트라넷 침투 서버는 공용 IP가 있는 시스템에 배포됩니다.
2. 인트라넷 서비스 및 인트라넷 침투 클라이언트는 인트라넷 시스템에 배포됩니다.
설명:
내 생각은 매우 간단합니다. 즉, 사용자가 인트라넷 침투 서버에 액세스하면 인트라넷 침투 서버가 사용자의 요청 메시지를 인트라넷 침투 클라이언트로 전달한 다음 인트라넷으로 전달한다는 것입니다. 침투 서버 네트워크 침투 클라이언트는 요청 메시지를 인트라넷 서비스로 전달한 후, 인트라넷 서비스로부터 응답 메시지를 받아 인트라넷 침투 서버로 전달하고, 마지막으로 인트라넷 침투 서버는 이를 사용자에게 전달합니다. 일반적인 과정은 이렇습니다. 외부 사용자의 경우 블랙박스 시스템을 마주하기 때문에 외부 네트워크 서비스에 접속했다고만 생각하게 됩니다.
위 목표를 달성하기 위해 가장 중요한 것은 인트라넷 침투 클라이언트와 인트라넷 침투 서버 간의 긴 연결을 유지하는 것입니다 이 긴 연결을 사용하여 양쪽에서 보고서를 교환해야 합니다. 파티.텍스트 정보. 따라서 이 긴 연결은 시스템이 시작된 후에 설정되어야 하며, 사용자 요청이 들어오면 인트라넷 침투 서버가 먼저 요청을 받은 다음 긴 연결을 사용하여 이를 네트워크 침투 클라이언트로 전송합니다. 이 메시지를 인트라넷 서비스에 접속하기 위한 요청으로 사용하고 인트라넷 서비스로부터 응답을 받아 인트라넷 침투 서버로 전달한 후 최종적으로 사용자에게 전달한다.
3. 코드 구현3.1 디렉토리 구조 설명: 인트라넷 침투를 위한 서버 및 클라이언트 코드입니다. 두 당사자 모두 공통된 유형을 사용해야 하기 때문에 별도로 작성하지 않았습니다. 다만, 별도로 배포해야 하기 때문에 두 개의 프로젝트로 분리하는 것이 좋습니다.또는 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; } }
내부망 침투 서버와 내부망 침투 클라이언트를 기동한 뒤 브라우저에서 URL 3개에 접속합니다. 참고: 1. 직접 테스트할 경우 실행 중인 인트라넷 침투 서버의 IP 주소로 전환하거나 도메인 이름을 사용할 수 있습니다. 2. 여기서 외부 네트워크 시스템과 내부 네트워크 시스템은 서로 다른 포트를 사용합니다(자신의 시스템에 있는 서비스 포트와 충돌하지 않는 한 자연스럽게 사용하십시오). 실제로 외부 네트워크에서 포트 80을 사용할 수 있습니다. 일반 사용자에게 더 친숙합니다. 3. 세 번째 테스트는 실제로 실패했습니다. 위의 로딩 애니메이션이 계속 로딩되는 것을 볼 수 있습니다. 이 일이 곧 중단되어야 한다는 것은 당연한 일이지만 중단하는 것은 불가능해 보입니다. 시스템 버그인데 지식이 부족해서 해결은 못하겠습니다.
여기 코드는 시뮬레이션이므로 이 함수만 시뮬레이션할 수 있지만 기본적으로 실제 효과는 없습니다. 하하. 여기는 긴 연결이 하나밖에 없기 때문에 그냥 한 사람이 호출하는 것이 가장 좋은 것 같습니다. 클라이언트와 서버 간의 연결 풀을 유지하여 멀티스레드 액세스가 가능하도록 하는 방법을 생각했습니다. 여기에서는 TCP 패킷 고정 및 하위 패킷화를 처리하지 않으므로(이 개념은 이해하지만 처리를 잘 못합니다.) 요청 및 응답 메시지 크기를 기본적으로 2KB 이내로 설정합니다. 이 길이를 초과하면 문제가 발생합니다. 이 매개변수를 늘릴 수는 있지만 대부분의 패킷이 매우 작으면 효율성도 낮아집니다. 이러한 인트라넷 침투는 적어도 이론상으로는 HTTP가 아닌 TCP 이상의 다양한 프로토콜을 지원할 수 있습니다.
위 내용은 블랙박스에 인트라넷 침투를 달성하기 위해 Java 시뮬레이션을 사용하는 방법은 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!