欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java使用黑盒方式模擬實(shí)現(xiàn)內(nèi)網(wǎng)穿透

 更新時(shí)間:2023年05月04日 09:01:30   作者:CrazyDragon_King  
這篇文章主要介紹了Java使用黑盒方式模擬實(shí)現(xiàn)內(nèi)網(wǎng)穿透,內(nèi)網(wǎng)穿透,也即 NAT 穿透,進(jìn)行 NAT 穿透是為了使具有某一個(gè)特定源 IP 地址和源端口號(hào)的數(shù)據(jù)包不被 NAT 設(shè)備屏蔽而正確路由到內(nèi)網(wǎng)主機(jī),需要的朋友可以參考下

前言:

最近準(zhǔn)備使用樹莓派搭建一個(gè)內(nèi)網(wǎng)監(jiān)控系統(tǒng),然后在外網(wǎng)訪問。因此選擇了釘釘內(nèi)網(wǎng)穿透的方式,因?yàn)檫@種方式最為簡(jiǎn)單,但是由于樹莓派的架構(gòu)是ARM指令集,所以無(wú)法運(yùn)行成功,釘釘內(nèi)網(wǎng)穿透只能在我的X86筆記本上面運(yùn)行了。但是我倒是對(duì)內(nèi)網(wǎng)穿透這個(gè)概念特別感興趣了,所以就想著能不能利用自己所學(xué)習(xí)的知識(shí),自己來(lái)模擬實(shí)現(xiàn)一個(gè)內(nèi)網(wǎng)穿透工具。

1. 內(nèi)網(wǎng)穿透簡(jiǎn)介

從黑盒的角度理解: 通常個(gè)人電腦無(wú)論是連接WIFI上網(wǎng)還是用網(wǎng)線上網(wǎng),都是屬于局域網(wǎng)里邊的,外網(wǎng)無(wú)法直接訪問到你的電腦,內(nèi)網(wǎng)穿透可以讓你的局域網(wǎng)中的電腦實(shí)現(xiàn)外網(wǎng)訪問功能。舉一個(gè)例子: 你在本地運(yùn)行了一個(gè)Web服務(wù),占用端口是8080,那么你本地進(jìn)行測(cè)試就是://localhost:8080。但是如果你想給一個(gè)好朋友分享你的服務(wù),那怎么辦呢?是的,就是采用內(nèi)網(wǎng)穿透的方式。 實(shí)際上,內(nèi)網(wǎng)穿透是很復(fù)雜的一個(gè)操作,百度百科上面的解釋為:

內(nèi)網(wǎng)穿透,也即 NAT 穿透,進(jìn)行 NAT 穿透是為了使具有某一個(gè)特定源 IP 地址和源端口號(hào)的數(shù)據(jù)包不被 NAT 設(shè)備屏蔽而正確路由到內(nèi)網(wǎng)主機(jī)。

這里,我顯然是做不了的。我需要的只是從外網(wǎng)訪問到內(nèi)網(wǎng)的服務(wù),至于具體的過(guò)程我不關(guān)心,只需要達(dá)到這個(gè)目的即可了。

2. 具體想法和實(shí)現(xiàn)細(xì)節(jié)

2.1 具體想法

無(wú)論是哪種方式實(shí)現(xiàn)內(nèi)網(wǎng)穿透都是需要一個(gè)公網(wǎng)IP地址的,我這里使用的一臺(tái)阿里云的服務(wù)器。下面是整個(gè)模擬的示意圖:

在這里插入圖片描述

注:

1.內(nèi)網(wǎng)穿透服務(wù)端部署在具有公網(wǎng)IP的機(jī)器上。

2.內(nèi)網(wǎng)服務(wù)和內(nèi)網(wǎng)穿透客戶端部署在內(nèi)網(wǎng)機(jī)器上。

說(shuō)明:

我的想法很簡(jiǎn)單,即用戶訪問內(nèi)網(wǎng)穿透服務(wù)器,然后內(nèi)網(wǎng)穿透服務(wù)器將用戶的請(qǐng)求報(bào)文轉(zhuǎn)發(fā)給內(nèi)網(wǎng)穿透客戶端,接著內(nèi)網(wǎng)穿透客戶端將請(qǐng)求報(bào)文轉(zhuǎn)發(fā)給內(nèi)網(wǎng)服務(wù),然后接收內(nèi)網(wǎng)服務(wù)的響應(yīng)報(bào)文,將其轉(zhuǎn)發(fā)給內(nèi)網(wǎng)穿透服務(wù)端,最后由內(nèi)網(wǎng)穿透服務(wù)端將其轉(zhuǎn)發(fā)給用戶。大致流程是這樣的,對(duì)于外部的用戶來(lái)說(shuō)它只會(huì)認(rèn)為它訪問了一個(gè)外網(wǎng)服務(wù),因?yàn)橛脩裘鎸?duì)的是一個(gè)黑盒系統(tǒng)。

2.2 實(shí)現(xiàn)細(xì)節(jié)

為了實(shí)現(xiàn)上面那個(gè)目標(biāo),其中最為關(guān)鍵的就是維持內(nèi)網(wǎng)穿透客戶端和內(nèi)網(wǎng)穿透服務(wù)端的一個(gè)長(zhǎng)連接,我需要使用這個(gè)長(zhǎng)連接來(lái)交換雙方的報(bào)文信息。因此,這個(gè)長(zhǎng)連接需要在系統(tǒng)啟動(dòng)后就建立好,當(dāng)有用戶的請(qǐng)求進(jìn)來(lái)的時(shí)候,內(nèi)網(wǎng)穿透服務(wù)端首先接收這個(gè)請(qǐng)求,然后使用長(zhǎng)連接將其轉(zhuǎn)給內(nèi)網(wǎng)穿透客戶端,內(nèi)網(wǎng)穿透客戶端使用該報(bào)文作為請(qǐng)求訪問內(nèi)網(wǎng)服務(wù),然后接收內(nèi)網(wǎng)服務(wù)的響應(yīng),將其轉(zhuǎn)發(fā)給內(nèi)網(wǎng)穿透服務(wù)端,最后將其轉(zhuǎn)發(fā)給用戶。

3. 代碼實(shí)現(xiàn)

3.1 目錄結(jié)構(gòu)

在這里插入圖片描述說(shuō)明: 這個(gè)是內(nèi)網(wǎng)穿透的服務(wù)端和客戶端代碼,我是放在一起了,沒有分開寫,因?yàn)殡p方需要使用到一些公用的類。但是建議還是分開成兩個(gè)工程,因?yàn)樾枰珠_部署。或者導(dǎo)出成jar包的時(shí)候,分別選擇不同的主類即可。

客戶端代碼文件:Client.java、Connection.java、Msg.java、ProxyConnection.java。
服務(wù)端代碼文件:Server.java、Connection.java、Msg.java、ProxyConnection.java。

3.2 Client 類

package org.dragon;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
/**
 * 用于雙向通信的客戶端
 * */
public class Client {
	private static final String REMOTE_HOST = "公網(wǎng)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);  // 維持和內(nèi)網(wǎng)穿透服務(wù)端的長(zhǎng)連接
			// 可以實(shí)現(xiàn)同一個(gè)人多次訪問
			while (true) {
				Msg msg = proxyConnection.receiveMsg();
				Connection connection = new Connection(new Socket(LOCAL_HOST, 8080));
				connection.sendMsg(msg);    // 將請(qǐng)求報(bào)文發(fā)送給內(nèi)網(wǎng)服務(wù)器,即模擬發(fā)送請(qǐng)求報(bào)文
				msg = connection.receiveMsg();  // 接收內(nèi)網(wǎng)服務(wù)器的響應(yīng)報(bào)文
				proxyConnection.sendMsg(msg);  // 將內(nèi)網(wǎng)服務(wù)器的響應(yīng)報(bào)文轉(zhuǎn)發(fā)給公網(wǎng)服務(wù)器(內(nèi)網(wǎng)穿透服務(wù)端)
			}
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

3.3 Connection 類

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;
/**
 * 維持用戶和服務(wù)器的連接
 * */
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();  // 每一次寫入都要刷新,防止阻塞。
	}
}

3.4 Msg 類

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);
	}
}

3.5 ProxyConnection 類

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
 * 
 * 代理服務(wù)器和代理客戶端是用于維持兩者之間通信的一個(gè)長(zhǎng)連接Socket,
 * 主要的目的是因?yàn)殡p方之間的通信方式是全雙工的,它們的作用是為了傳遞報(bào)文。
 * */
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()));
	}
	/**
	 * 接收?qǐng)?bào)文
	 * @throws IOException 
	 * */
	public Msg receiveMsg() throws IOException {
		int len = input.readInt();
		if (len <= 0) {
			throw new IOException("異常接收數(shù)據(jù),長(zhǎng)度為:" + len);
		}
		byte[] msg = new byte[len];
		int size = input.read(msg);  // 這里到底會(huì)不會(huì)讀取到這么多,我也有點(diǎn)迷惑!
		return new Msg(size, msg);   // 為了防止出錯(cuò),還是使用一個(gè)記錄實(shí)際讀取值size
	}
	/**
	 * 轉(zhuǎn)發(fā)報(bào)文
	 * @throws IOException 
	 * */
	public void sendMsg(Msg msg) throws IOException {
		output.writeInt(msg.getLen());
		output.write(msg.getMsg(), 0, msg.getLen());
		output.flush();  // 每一次寫入都需要手動(dòng)刷新,防止阻塞。
	}
}

3.6 Server 類

package org.dragon;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
 * 用于雙向通信的服務(wù)器
 * */
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();  // 接收用戶的請(qǐng)求報(bào)文
				proxySocket.sendMsg(msg);           // 轉(zhuǎn)發(fā)用戶的請(qǐng)求報(bào)文給內(nèi)網(wǎng)服務(wù)器
				msg = proxySocket.receiveMsg();     // 接收內(nèi)網(wǎng)服務(wù)器的響應(yīng)報(bào)文
				connection.sendMsg(msg);            // 轉(zhuǎn)發(fā)內(nèi)網(wǎng)服務(wù)器的響應(yīng)報(bào)文給用戶
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

4. 內(nèi)網(wǎng)服務(wù)

內(nèi)網(wǎng)服務(wù)是一個(gè)web服務(wù),這里我使用的是一個(gè)簡(jiǎn)單的SpringBoot項(xiàng)目,它只有三個(gè)請(qǐng)求方法。

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 "有一美人兮,見之不忘。一日不見兮,思之如狂。鳳飛翱翔兮,四海求凰。無(wú)奈佳人兮,不在東墻。";
	}
	@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", "有一美人兮,見之不忘。一日不見兮,思之如狂。"
				+ "鳳飛翱翔兮,四海求凰。無(wú)奈佳人兮,不在東墻。");
		return map;
	}
}

5. 測(cè)試

5.1 內(nèi)網(wǎng)測(cè)試

啟動(dòng)內(nèi)網(wǎng)服務(wù),在瀏覽器輸入以下三條URL進(jìn)行測(cè)試,功能正常。 在這里插入圖片描述在這里插入圖片描述

在這里插入圖片描述

5.2 外網(wǎng)測(cè)試

先后啟動(dòng)內(nèi)網(wǎng)穿透服務(wù)端和內(nèi)網(wǎng)穿透客戶端,然后在瀏覽器訪問一下三條URL即可。 注意: 1.如果你自己測(cè)試,切換成你運(yùn)行內(nèi)網(wǎng)穿透服務(wù)器的ip地址或者使用域名也行。 2.我這里外網(wǎng)機(jī)器和內(nèi)網(wǎng)機(jī)器使用的是不同的端口(隨便使用,只要不和自己機(jī)器上的服務(wù)端口沖突就行了),實(shí)際上可以在外網(wǎng)使用80端口,這樣對(duì)普通用戶比較友好。 3.第三條測(cè)試實(shí)際上是失敗的,可以看到上面那個(gè)加載動(dòng)畫,一直在加載。按理說(shuō)這個(gè)應(yīng)該很快就停止了,但是似乎無(wú)法停下來(lái)。這是系統(tǒng)的bug了,但是由于我掌握的知識(shí)有限,就不去解決了。

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

6. 注意事項(xiàng)

這里的代碼是一種模擬,它只能模擬這個(gè)功能,但是基本上不具備實(shí)際的作用,哈哈。因?yàn)槲疫@里只有一個(gè)長(zhǎng)連接,所以只能支持串行的通信,最好就是一個(gè)人簡(jiǎn)單的調(diào)用,似乎調(diào)用速度也不能太快了。我想了一種方式,在客戶端和服務(wù)器之間維持一個(gè)連接池,這樣就可以實(shí)現(xiàn)多線程訪問了。這里沒有處理TCP的粘包和分包(我理解了這個(gè)概念,但是我不太會(huì)處理它),所以我默認(rèn)請(qǐng)求報(bào)文和響應(yīng)報(bào)文都是2KB以內(nèi)大小。如果超過(guò)這個(gè)長(zhǎng)度會(huì)導(dǎo)致問題,盡管可以調(diào)大這個(gè)參數(shù),但是如果多數(shù)報(bào)文的都是很小的話,也會(huì)導(dǎo)致效率低下。這個(gè)內(nèi)網(wǎng)穿透是可以支持TCP之上的各種協(xié)議的,不一定是HTTP,至少理論上是可以的。

7. 總結(jié)

從這個(gè)想法的萌生到實(shí)現(xiàn)這個(gè)功能也是花了我好幾天時(shí)間的,這本身就是一個(gè)學(xué)習(xí)的過(guò)程。學(xué)習(xí)使用自己的網(wǎng)絡(luò)和編程知識(shí)解決問題,我認(rèn)為這是一種很好的學(xué)習(xí)方式——學(xué)以致用。書到用時(shí)方恨少,在這個(gè)過(guò)程中體現(xiàn)的淋漓盡致,想要達(dá)到某個(gè)目的,但是由于自己知識(shí)的不足,沒有什么特別好的解決辦法,只能采用一些不優(yōu)雅的實(shí)現(xiàn)方式了。不過(guò),辯證的看,這也是一件好事,至少它指明了下一步學(xué)習(xí)的方向。

到此這篇關(guān)于Java使用黑盒方式模擬實(shí)現(xiàn)內(nèi)網(wǎng)穿透的文章就介紹到這了,更多相關(guān)Java黑盒實(shí)現(xiàn)內(nèi)網(wǎng)穿透內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Servlet方法生命周期及執(zhí)行原理詳解

    Servlet方法生命周期及執(zhí)行原理詳解

    運(yùn)行在服務(wù)器端的小程序,Servlet就是一個(gè)接口,定義了Java類被瀏覽器訪問到(tomcat識(shí)別)的規(guī)則,將來(lái)我們自定義一個(gè)類,實(shí)現(xiàn)Servlet接口,復(fù)寫方法
    2021-09-09
  • Mybatis-Plus-AutoGenerator 最詳細(xì)使用方法

    Mybatis-Plus-AutoGenerator 最詳細(xì)使用方法

    這篇文章主要介紹了Mybatis-Plus-AutoGenerator 最詳細(xì)使用方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • Java多線程實(shí)戰(zhàn)之單例模式與多線程的實(shí)例詳解

    Java多線程實(shí)戰(zhàn)之單例模式與多線程的實(shí)例詳解

    今天小編就為大家分享一篇關(guān)于Java多線程實(shí)戰(zhàn)之單例模式與多線程的實(shí)例詳解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-02-02
  • Java定時(shí)任務(wù)的三種實(shí)現(xiàn)方法

    Java定時(shí)任務(wù)的三種實(shí)現(xiàn)方法

    在應(yīng)用里經(jīng)常都有用到在后臺(tái)跑定時(shí)任務(wù)的需求。舉個(gè)例子,比如需要在服務(wù)后臺(tái)跑一個(gè)定時(shí)任務(wù)來(lái)進(jìn)行垃圾回收
    2014-04-04
  • mybatis-plus中的Enum用法實(shí)例

    mybatis-plus中的Enum用法實(shí)例

    本文主要介紹了mybatis-plus中的Enum用法實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • javaSE中異常如何處理舉例詳解

    javaSE中異常如何處理舉例詳解

    程序運(yùn)行過(guò)程中發(fā)生了不正常的情況,這種不正常的情況叫做異常,下面這篇文章主要給大家介紹了關(guān)于javaSE中異常如何處理的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-01-01
  • Java并發(fā)之異步的八種實(shí)現(xiàn)方式

    Java并發(fā)之異步的八種實(shí)現(xiàn)方式

    本文主要介紹了Java并發(fā)之異步的八種實(shí)現(xiàn)方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • Java創(chuàng)建多線程服務(wù)器流程

    Java創(chuàng)建多線程服務(wù)器流程

    這篇文章主要介紹了Java創(chuàng)建多線程服務(wù)器流程,以下實(shí)例演示了如何使用Socket類的accept()方法和ServerSocket類的MultiThreadServer(socketname)方法來(lái)實(shí)現(xiàn)多線程服務(wù)器程序
    2023-05-05
  • Java CompletableFuture 異步超時(shí)實(shí)現(xiàn)深入研究

    Java CompletableFuture 異步超時(shí)實(shí)現(xiàn)深入研究

    這篇文章主要為大家介紹了Java CompletableFuture 異步超時(shí)實(shí)現(xiàn)深入研究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • java多線程讀取多個(gè)文件的方法

    java多線程讀取多個(gè)文件的方法

    這篇文章主要為大家詳細(xì)介紹了java多線程讀取多個(gè)文件的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-08-08

最新評(píng)論