Android 單執行緒也能極速重新整理並獲取區域網裝置(IP+MAC)資訊, 從4分30S 優化到 0.150S 不是夢
提前說明,Android 獲取區域網線上裝置的大概原理是:先去檢測某ip是否能通訊,然後去 /proc/net/arp 檔案(簡稱arp檔案)下讀取裡面正確的mac 與 ip 。
最近產品又提需求了,要顯示當前區域網所在的裝置數量以及相關IP和mac資訊,心裡YY一笑,這種功能太簡單,
以前也研究過,只是方法(ping)比較耗時,我就把這情況告訴了產品,建議加個正在獲取的效果,產品否決,獲取過程
不能超過3s,話說用Ping方法在3s內要拿到結果,我得開幾十個執行緒才行吧,果斷放棄了這個想法,畢竟不是PC應用,
先上一個ping的方法
這就是ping方法 在單執行緒下,從 ip尾段 2~254 全部ping完,最少也要4分多,waitFor()方法是耗時的,它需要等待ping的響private void pingIp(String ip){ try { java.lang.Process process = Runtime.getRuntime().exec("ping -c 1 -w 10 " + ip); process.waitFor(); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } }
應結果,響應完全部後還是要去讀arp檔案,是不是覺得這個流程有點奇怪,我為何要等他響應?等你響應完,從響應結果也拿
不到有價值的資訊,所以得想辦法不要這個響應過程。 那麼問題來了,你發ping出去,又不想要等響應,這個ping能實現?我
從ping的help中看了下其他附帶屬性,貌似沒有能達到這種結果的做法。
後來重新想了下Android的儲存線上裝置資訊的原理,它是先和其他IP通訊,如果要通訊的那個IP正常響應,那就儲存對方的信
息到該檔案,下次再和該IP通訊時,就能快速地通過該資訊去找到這個要通訊的裝置了,這整個過程和應用層無關,這就涉及到計算
機網路知識了,小編了解不是很多,為了不誤導大家,所以具體網路層這塊還是自己去了解吧,回到主題,既然與我們應用層無關,
那麼我們是不是可以對某個IP發一個訊息,然後坐等網路層處理,我們不管這事情了呢,說到這裡,你是不是想到了什麼?不錯,就
是UDP,直接向某IP地址發訊息包過去(反正我發給你了,我可不管了) ,上程式碼
直接向尾段為 2~254 的IP發個訊息包過去,為了保證快速傳送,所以這個訊息包大小就為0,有小白可能就疑惑了,你這用的DatagramPacket dp = new DatagramPacket(new byte[0], 0, 0); DatagramSocket socket = new DatagramSocket(); int position = 2; while (position < 255) { dp.setAddress(InetAddress.getByName("192.168.1." + String.valueOf(position))); socket.send(dp); } socket.close();
是埠啊,你這訊息要發給誰啊,這也只能說去了解下計算機網路知識和使用埠的含義,粗糙得解釋,埠主要是為了區分一個IP
地址的其他socket或者說應用,所以說可以不考慮埠,訊息我發到該IP地址了,說明能通訊,只是有沒有這個埠的socket或應用
來接收,就不關我的事了。
有人可能就直接用上面的方法去測試,不要急,先看下去,否則你會回來罵我的,說好的極速呢、0.150s呢,為何我的總是在
3.2s左右,你這不是騙我嗎,我沒有騙你,只是你太心急了而已,如果你細心除錯上面的程式碼或者看他傳送的時間,你就會發現,在
發訊息到尾段 236~239的IP時候(可能是其他區間),在某個IP傳送完訊息後出現停頓2s左右後才繼續發,這是什麼原因?這裡我只
猜測DatagramSocket 可能有一個排隊的現象(只是猜測,如果你知道具體原因,可以留言),直接上解決方法:
DatagramPacket dp = new DatagramPacket(new byte[0], 0, 0);
DatagramSocket socket = new DatagramSocket();
int position = 2;
while (position < 255) {
dp.setAddress(InetAddress.getByName("192.168.1." + String.valueOf(position)));
socket.send(dp);
position++;
if (position == 125) {//分兩段掉包,一次性發的話,達到236左右,會耗時3秒左右再往下發
socket.close();
socket = new DatagramSocket();
}
}
socket.close();
解釋已經寫在上面程式碼塊了。跑完之後,再去讀arp檔案後就可以了,小編測試過,從發完包到過濾讀取arp資訊,平均耗時0.150s
左右,但是也會出現發完全部包後arp檔案還是沒有更新的現象,這情況只能說網路層響應慢或者更新慢(個人除錯發現,基本不會出現
超過一秒才能讀到的情況),所以只能來個輪巡查詢
AllUtils.initAreaIp(mContext);//這是發包的方法
List<AreaDeviceBean> beans = new ArrayList<>();
int sum = 0;
while (beans.size() == 0 && sum < 10) {
beans.addAll(AllUtils.getAllCacheMac(mLocalIP));//讀取arp內容方法
SystemClock.sleep(beans.size()>0?0:500);
sum++;
}
上面輪巡方法只提供參考,附帶Demo,小編辛苦打字,如果對你有幫助,請點個贊好嗎,如果有錯,請留言指出!