摘要:前言今天,我將梳理在網(wǎng)絡(luò)編程中很重要的一個(gè)類以及其相關(guān)的類。這類主機(jī)通常不需要外部互聯(lián)網(wǎng)服務(wù),僅有主機(jī)間相互通訊的需求。可以通過該接口獲取所有本地地址,并根據(jù)這些地址創(chuàng)建。在這里我們使用阻塞隊(duì)列實(shí)現(xiàn)主線程和打印線程之間的通信。
前言
今天,我將梳理在Java網(wǎng)絡(luò)編程中很重要的一個(gè)類InetAddress以及其相關(guān)的類NetworkInterface。在這篇文章中將會(huì)涉及:
InetAddress
NetworkInterface
具體應(yīng)用范例
這里的范例將會(huì)實(shí)現(xiàn)一個(gè)簡單的日志IP解析系統(tǒng)。我們將會(huì)在后面詳細(xì)介紹。
InetAddress API在此我將會(huì)直接從API入手,如果對(duì)其中的名詞有何疑惑,可以先行了解一下基礎(chǔ)概念。
需要先行了解的概念包括:
IP,IPv4,IPv6
單播,多播,廣播
loop地址
域名
public class InetAddress{ //私有化構(gòu)造器,我們只能通過其提供的靜態(tài)工廠方法來獲取一個(gè)實(shí)例 //同時(shí)我們也不可以修改內(nèi)部的IP地址或是域名 //這種Immutable的方式確保了其線程安全性 //這里需要注意java沒有正整數(shù)型,因此我們需要對(duì)開頭為1的二進(jìn)制數(shù)進(jìn)行轉(zhuǎn)義,如下 //byte[] address = {107, 23, (byte) 216, (byte) 196}; public static InetAddress getByAddress(byte[] addr) throws UnknownHostException; //該方法不會(huì)查詢DNS來確保域名和IP地址相符 //這里有一個(gè)比較有意思的在于如果你希望尋找家用設(shè)備如打印機(jī)等的局域網(wǎng)地址,則可以通過遍歷254個(gè)可能的局域網(wǎng)地址來找到它,而不需要將其硬編碼進(jìn)代碼中 public static InetAddress getByAddress(String host, byte[] addr) throws UnknownHostException; //根據(jù)域名獲取一個(gè)實(shí)例,該加載為懶加載,即除非到必要的時(shí)候,否則將不會(huì)啟動(dòng)DNS查詢 public static InetAddress getByName(String host) throws UnknownHostException; //獲取該域名對(duì)應(yīng)的所有IP地址(即一個(gè)域名可以映射到多個(gè)IP地址) public static InetAddress[] getAllByName(String host) throws UnknownHostException //獲取環(huán)回地址,通常為172.0.0.1 public static InetAddress getLoopbackAddress(); //獲取本機(jī)的IP地址,如果本機(jī)沒有聯(lián)網(wǎng),則返回環(huán)回地址 public static InetAddress getLoaclHost(); //獲取主機(jī)名 public String getHostName() //獲取主機(jī)別名 public String getCanonicalHostName() //獲取主機(jī)的IP地址 //該方法可以用來判斷是IPv4地址還是IPv6地址 public byte[] getAddress() //獲取String類型的IP地址 public String getHostAddress() //是否是一個(gè)和本地主機(jī)相關(guān)的地址 //包括本機(jī)IP,loopback,wifi地址等 public boolean isAnyLocalAddress() //是否是環(huán)回地址 public boolean isLoopbackAddress() //是否是鏈路本地地址 //鏈路本地地址(Link-local address)是計(jì)算機(jī)網(wǎng)絡(luò)中一類特殊的地址, //它僅供于在網(wǎng)段,或廣播域中的主機(jī)相互通信使用。 //這類主機(jī)通常不需要外部互聯(lián)網(wǎng)服務(wù),僅有主機(jī)間相互通訊的需求。 public boolean isLinkLocalAddress() //是否落入這三個(gè)網(wǎng)段 //10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16. public boolean isSiteLocalAddress() //是否是廣播地址 public boolean isMulticastAddress() //是否是全球通用的廣播地址 public boolean isMCGlobal() //是否是企業(yè)專屬的多播地址 public boolean isMCOrgLocal() //判斷當(dāng)前節(jié)點(diǎn)是否可以訪問目標(biāo)節(jié)點(diǎn) //通常使用ICMP報(bào)文實(shí)現(xiàn) public boolean isReachable(int timeout) throws IOException public boolean isReachable(NetworkInterface interface, int ttl, int timeout) throws IOException //只要兩個(gè)對(duì)象的IP地址相等,則二者相等(與域名無關(guān)) public boolean equals(Object o) //僅根據(jù)IP地址計(jì)算hashCode public int hashCode() //格式為 “域名/IP地址” public String toString() }
相關(guān)參數(shù):
networkaddress.cache.negative.ttl:失敗的DNS查找被緩存的時(shí)間
networkaddress.cache.ttl:成功的DNS查找被緩存的時(shí)間
NetworkInterface代表一個(gè)本地的IP地址??梢酝ㄟ^該接口獲取所有本地地址,并根據(jù)這些地址創(chuàng)建InetAddress。通過NetworkInterface接口,可以獲取本機(jī)配置的網(wǎng)絡(luò)接口的名字,IP列表(包括IPV4和IPV6),網(wǎng)卡地址,最大傳輸單元(MTU),接口狀態(tài)(開啟/關(guān)閉)、接口網(wǎng)絡(luò)類型(點(diǎn)對(duì)點(diǎn)網(wǎng)絡(luò)、回環(huán)網(wǎng)絡(luò))等信息
public final class NetworkInterface{ //根據(jù)名稱獲取網(wǎng)絡(luò)接口 //該方法依賴于底層平臺(tái) public static NetworkInterface getByName(String name) throws SocketException //根據(jù)IP地址獲得對(duì)應(yīng)的網(wǎng)絡(luò)接口 public static NetworkInterface getByInetAddress(InetAddress address) throws SocketException //獲取所有的網(wǎng)絡(luò)接口 public static Enumeration getNetworkInterfaces() throws SocketException //獲取該接口之下所有的IP地址 public Enumeration getInetAddresses() //獲取MAC地址 public byte[] getHardwareAddress() }獲取WebLog中的IP地址對(duì)應(yīng)的hostName
當(dāng)我們啟動(dòng)web應(yīng)用時(shí),我們往往會(huì)從HTTP報(bào)文中獲取訪客的IP并且將其記錄進(jìn)日志中。從而可以從日志中分析常見的訪客或是不明訪客。這里我們將獲取一個(gè)手動(dòng)生成的包含IP地址的日志文件,并且將其中的IP地址轉(zhuǎn)化為hostName輸出。這里我們將采用多線程實(shí)現(xiàn),因?yàn)樽x取一條IP記錄的速度遠(yuǎn)遠(yuǎn)高于從DNS服務(wù)器獲取域名的速度。
首先我們使用工具類向文件中寫入日志:
public class Util { private static final String filePath = "YOUR_FILE_PATH"; private static final String[] hostNames = new String[]{ "www.sina.com", "www.sohu.com", "www.taobao.com", "www.baidu.com", "www.qq.com", "www.163.com", "www.dzone.com", "www.github.com", "www.acmcoder.com", "www.meituan.com", "kafka.apachecn.org", "www.ibm.com", "javarevisited.blogspot.kr" }; public static void main(String[] args){ int count = 0; try (BufferedWriter bf = new BufferedWriter(new FileWriter(filePath))){ for(String host : hostNames){ InetAddress[] addresses = InetAddress.getAllByName(host); count+= addresses.length; for (InetAddress address : addresses) { bf.write(address.getHostAddress() + " " + address.getHostName()); bf.newLine(); } } System.out.println(count); } catch (IOException e) { e.printStackTrace(); } } }
一個(gè)處理LOG的IPAnalyser類,該類實(shí)現(xiàn)Callable接口,支持將內(nèi)部的值返回給別的線程使用:
public class IPAnalyser implements Callable{ private String logItem; public IPAnalyser(String logItem){ this.logItem = logItem; } @Override public String call() throws Exception { if (logItem!=null){ String[] items = logItem.split(" "); InetAddress inetAddress = InetAddress.getByName(items[0]); return inetAddress.getHostAddress(); } return null; } }
額外使用一個(gè)線程來同步處理IPAnalyser返回的值,從而避免因?yàn)槿罩疚募^大,日志條目全部存儲(chǔ)在主存中,再一次性讀取而帶來因占用大量內(nèi)存而影響性能的問題。
在這里我們使用阻塞隊(duì)列實(shí)現(xiàn)主線程和打印線程之間的通信。但是我們需要在打印完該日志文件后,結(jié)束該打印線程,因此使用write來記錄當(dāng)前已經(jīng)打印的日志條目數(shù),然后在打印完成所有的之后結(jié)束該線程。
public class IPPrinter implements Runnable { private BlockingQueuequeue; private static volatile int writeCount; public IPPrinter(BlockingQueue queue){ this.queue = queue; } @Override public void run() { while (true){ if (writeCount == Main.readCount){ System.out.println("寫完畢"); break; } try { LogEntry logEntry = queue.take(); System.out.println(logEntry.getOrigin() + "對(duì)應(yīng)的主機(jī)名為" + logEntry.getHostName().get()); writeCount++; } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } }
實(shí)體類LogEntry保存原來的日志條目和解析后的日志線程返回:
public class LogEntry { private FuturehostName; private String origin; public LogEntry(Future hostName, String origin){ this.hostName = hostName; this.origin = origin; } public void setOrigin(String origin) { this.origin = origin; } public String getOrigin() { return origin; } public Future getHostName() { return hostName; } public void setHostName(Future hostName) { this.hostName = hostName; } }
最后在主線程中開啟IO和相關(guān)的所有線程:
public class Main { private static final String filePath = "/Users/rale/IdeaProjects/Demo/concurrency/src/main/java/cn/deerowl/weblog_analyse/web.log"; private static final BlockingQueuequeue = new LinkedBlockingQueue<>(); public static volatile int readCount = 30; public static void main(String[] args){ ExecutorService executorService = Executors.newFixedThreadPool(6); executorService.submit(new IPPrinter(queue)); try (BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath))){ String tmp; while ((tmp = bufferedReader.readLine()) != null){ Future f = executorService.submit(new IPAnalyser(tmp)); queue.put(new LogEntry(f, tmp)); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } executorService.shutdown(); } }
這里要強(qiáng)調(diào)一下域名和主機(jī)名的關(guān)聯(lián):一個(gè)域名之下可以有多個(gè)主機(jī)名,如域名abc.com下,有主機(jī)server1和server2,其主機(jī)全名就是server1.abc.com和server2.abc.com。
參考書籍Java Network Programming 4th Edition
想要了解更多開發(fā)技術(shù),面試教程以及互聯(lián)網(wǎng)公司內(nèi)推,歡迎關(guān)注我的微信公眾號(hào)!將會(huì)不定期的發(fā)放福利哦~
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/76366.html
摘要:不同類型的流入,往往對(duì)應(yīng)于不同類型的流數(shù)據(jù)。所以通常會(huì)將字節(jié)緩存到一定數(shù)量后再發(fā)送。如果是,則將兩個(gè)標(biāo)記都拋棄并且將之前的內(nèi)容作為一行返回。因此二者陷入死鎖。因此推出了和類。 前言 最近在重拾Java網(wǎng)絡(luò)編程,想要了解一些JAVA語言基本的實(shí)現(xiàn),這里記錄一下學(xué)習(xí)的過程。 閱讀之前,你需要知道 網(wǎng)絡(luò)節(jié)點(diǎn)(node):位于網(wǎng)絡(luò)上的互相連通的設(shè)備,通常為計(jì)算機(jī),也可以是打印機(jī),網(wǎng)橋,路由器等...
摘要:從而一方面減少了響應(yīng)時(shí)間,另一方面減少了服務(wù)器的壓力。表明響應(yīng)只能被單個(gè)用戶緩存,不能作為共享緩存即代理服務(wù)器不能緩存它。這種情況稱為服務(wù)器再驗(yàn)證。否則會(huì)返回響應(yīng)。 前言 本文將根據(jù)最近所學(xué)的Java網(wǎng)絡(luò)編程實(shí)現(xiàn)一個(gè)簡單的基于URL的緩存。本文將涉及如下內(nèi)容: HTTP協(xié)議 HTTP協(xié)議中與緩存相關(guān)的內(nèi)容 URLConnection 和 HTTPURLConnection Respo...
摘要:從而一方面減少了響應(yīng)時(shí)間,另一方面減少了服務(wù)器的壓力。表明響應(yīng)只能被單個(gè)用戶緩存,不能作為共享緩存即代理服務(wù)器不能緩存它。這種情況稱為服務(wù)器再驗(yàn)證。否則會(huì)返回響應(yīng)。 前言 本文將根據(jù)最近所學(xué)的Java網(wǎng)絡(luò)編程實(shí)現(xiàn)一個(gè)簡單的基于URL的緩存。本文將涉及如下內(nèi)容: HTTP協(xié)議 HTTP協(xié)議中與緩存相關(guān)的內(nèi)容 URLConnection 和 HTTPURLConnection Respo...
摘要:三端口與套接字端口指一臺(tái)計(jì)算機(jī)只有單一的連接到網(wǎng)絡(luò)的物理連接,所以的數(shù)據(jù)都通過此連接對(duì)內(nèi)對(duì)外送達(dá)特定的計(jì)算機(jī),這就是端口。三程序設(shè)計(jì)由上面可知基于的信息傳遞速度更快。接收數(shù)據(jù)包使用創(chuàng)建數(shù)據(jù)包套接字,綁定指定端口。 服務(wù)器 網(wǎng)絡(luò) 客戶機(jī) 第一部分 一.局域網(wǎng)與因特網(wǎng) 服務(wù)器是指提供信息的計(jì)算機(jī)或程序,...
摘要:網(wǎng)絡(luò)層主要將從下層接收到的數(shù)據(jù)進(jìn)行地址例的封裝與解封裝。會(huì)話層通過傳輸層端口號(hào)傳輸端口與接收端口建立數(shù)據(jù)傳輸?shù)耐贰? 第六階段 網(wǎng)絡(luò)編程 每一臺(tái)計(jì)算機(jī)通過網(wǎng)絡(luò)連接起來,達(dá)到了數(shù)據(jù)互動(dòng)的效果,而網(wǎng)絡(luò)編程所解決的問題就是如何讓程序與程序之間實(shí)現(xiàn)數(shù)據(jù)的通訊與互動(dòng)在嗎?你是GG還是MM? (一) 網(wǎng)絡(luò)模型概述 (1) 兩大模型 網(wǎng)絡(luò)模型一般是指: OSI(Open System Inter...
閱讀 1680·2021-10-29 13:11
閱讀 845·2021-09-22 10:02
閱讀 1702·2021-08-20 09:35
閱讀 1564·2019-08-30 15:54
閱讀 2470·2019-08-30 15:44
閱讀 1395·2019-08-29 16:52
閱讀 1111·2019-08-23 12:56
閱讀 769·2019-08-22 15:16