如何獲取本機IP


GetLocalHost

直接通過InetAddress.getLocalHost()來獲取,其主要邏輯如下

	InetAddress.getLocalHost();
	String hostname = impl.getLocalHostName();
	if(hostname.equals("localhost")){
		return impl.loopvacjAddress();
	}
	InetAddress.getAddressesFromNameService(hostname, null);
	nameService.lookupAllHostAddr(host);

在linux中的hostname是個變量,由系統初始化的時候, 在shell啟動腳本 “/etc/rc.d/rc.sysinit” 中實現,主要是讀取“/etc/sysconfig/network” 中的HOSTNAME的值 可以通過命令 hostname xxx 修改 hostname。

這里有幾個注意點:

1. 如果文件中沒有hostname,那么會使用默認的localhost

2. 如果發現hostname的值是localhost 或者 localhost.localdomain, 根據自己的實際ip查找/etc/hosts中這個ip對應的hostname。

3. 如果沒有,則使用localhost 或者localhost.localdomain

如果hostname是localhost,就會直接返回環回地址,如IPv4的127.0.0.1

如果不是的話,則會先看緩存里的CachedLocalHost的值,如果緩存的時間離現在小於5s的話,則直接返回緩存里的內容,如果間隔時間超過5s,則重新查詢

重新查詢是通過NameService去獲取IP地址的,具體的實現類是DNSNameService,其中NameServices是InetAddress是成員變量,通過static代碼塊初始化的

主要實現都是通過native的系統調用,查看/etc/resolv.conf下配置的nameserver和/etc/hosts下面的配置,然后使用DNS協議查詢,查詢后將其緩存。

如果DNS查詢不到的話,會拋出異常,UnKnownHostName。

一般來說,沒有自己去進行一些主動的配置的話,會就拿到127.0.0.1這種IP,顯然是無效IP

獲取所有網卡的IP

換另外一種思路,通過本機網絡設備所綁定的網卡來獲取本機的IP

	Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
	if (interfaces != null) {
		while (interfaces.hasMoreElements()) {
			try {
				NetworkInterface network = interfaces.nextElement();
				if(network.isVirtual()){
    				continue;/**如果是虛擬網卡,排除此網卡*/
  				}
  				Enumeration<InetAddress> addresses = network.getInetAddresses();
  				if (addresses != null) {
					while (addresses.hasMoreElements()) {
						try {
							InetAddress address = addresses.nextElement();
							if (isValidAddress(address)) {
								return address;
							}
						} catch (Throwable e) {
							LOGGER.warn("Failed to retriving ip address, " + e.getMessage(), e);
						}
					}
 	 			}
			} catch (Throwable e) {
  				LOGGER.warn("Failed to retriving ip address, " + e.getMessage(), e);
			}
		}
	}

這種方法就是,拿到的是所有網絡設備的屬性,假裝過濾虛擬網卡,找到第一個屬於有效IP的地址。

如何判斷是有效IP?

	LOCALHOST = "127.0.0.1";

	ANYHOST = "0.0.0.0";

	LOCAL_IP_PATTERN = Pattern.compile("127(\\.\\d{1,3}){3}$");

	IP_PATTERN = Pattern.compile("\\d{1,3}(\\.\\d{1,3}){3,5}$");

但是有一點劣勢就是,不知道哪塊才是真正用來和外界通信的網卡,比如我的開發機

就經常出現這個拿到192.168.122.1的情況,virbr0是一個虛擬網卡,可是java拿到他的時候,虛擬的屬性卻是false。

當然,這塊網卡可以卸載,不過不在討論范圍。

這也是一個問題,這個網卡明明是虛擬網卡,但是java拿到它的時候,屬性就不是虛擬的,沒辦法,誰讓這個接口實質性調用的是一個native的getAll方法。

通過連接遠程端口

最好的方式自然是通過Socket去連接一個遠程端口,這樣就能很方便地知道本機與外部通信時候使用的IP了。

	try {
		Socket socket = new Socket();
		try {
			SocketAddress addr = new InetSocketAddress(host, port);
			socket.connect(addr, 1000);
			return socket.getLocalAddress();
		} finally {
			try {
  				socket.close();
			} catch (Throwable e) {
			}
		}
	} catch (Exception e) {
		LOGGER.warn("Failed to retrive local address by connecting to dest host,ip={},port={},e={}", host, port, e);
	}

這種方式拿到的本機IP就比較保險了

當然, 比如你連接本機的端口,拿到的地址還會是127.0.0.1

連接本地局域網內的機器,拿到的會是本機局域網段的地址,比如我的機器是10.97.26.154

連接一個具有公網ip的機器的端口,拿到的還是本機局域網段的地址,比如我的機器是10.97.26.154

其實這個,還是也拿到網卡的地址,當你使用哪個網卡去連接此端口的時候,就會得到哪個網卡所綁定的地址。

IP地址綁定

服務啟動的時候,如果不確定應該綁定在哪個地址,則應該使用0.0.0.0,這樣的話,通過所有本機的網卡的地址,都能訪問此服務。

如果綁定的是127.0.0.1的話,那么只端口只對本機提供服務。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM