網路喚醒原理淺析(Wake On LAN)
之前我的一篇文章《網路喚醒全攻略(Wake On Lan)》介紹過如何設置遠程喚醒電腦,著重於使用,這篇主要從原理方面解析一下當中的奧妙;
原理
將喚醒魔術包發送的被喚醒機器的網卡上,魔術包指AMD公司開發的喚醒數據包,具有遠程喚醒的網卡都支持這個標準,用16進位表示如下:
6對「FF」前綴+16次重複MAC地址,舉個例子假如我的網卡MAC地址是:AA:BB:CC:DD:EE:FF:11
那麼魔術包就是:
0xFFFFFFFFFFAABBCCDDEEFF11AABBCCDDEEFF11AABBCCDDEEFF11AABBCCDDEEFF11AABBCCDDEEFF11AABBCCDDEEFF11AABBCCDDEEFF11AABBCCDDEEFF11AABBCCDDEEFF11AABBCCDDEEFF11AABBCCDDEEFF11AABBCCDDEEFF11AABBCCDDEEFF11AABBCCDDEEFF11AABBCCDDEEFF11AABBCCDDEEFF11
但是傳送的時候必須封包成二進位格式才可以傳送,簡單來說,我們抽2個區段分析:
FFFFFFFFFFF 轉成: 11111111 11111111 11111111 11111111 11111111 11111111AABBCCDDEEFF11 轉成:?10101010 10111011 11001100 11011101 11101110 11111111 00010001?
那麼封包後就是把每個位元組連接在一起:
11111111 11111111 11111111 11111111 11111111 11111111 10101010 10111011 11001100 11011101 11101110 11111111 00010001?……..10101010 10111011 11001100 11011101 11101110 11111111 00010001?(第16次)
開發實現
關鍵代碼(Java):
private String Wake(String name, String host, String mac, int port) { try { byte[] macBytes = getMacBytes(mac);//轉成位元組類型 byte[] bytes = new byte[6 + 16 * macBytes.length]; for (int i = 0; i < 6; i++) { bytes[i] = (byte) 0xff; } for (int i = 6; i < bytes.length; i += macBytes.length) { System.arraycopy(macBytes, 0, bytes, i, macBytes.length); //放入16個MAC地址 } InetAddress address = InetAddress.getByName(host); DatagramPacket packet = new DatagramPacket(bytes, bytes.length, address, port); DatagramSocket socket = new DatagramSocket(); socket.send(packet); socket.close(); return "wol_package_sent_success"; } catch (Exception e) { return "wol_package_sent_fail"; } }
private static byte[] getMacBytes(String mac) throws IllegalArgumentException { byte[] bytes = new byte[6]; String[] hex = mac.split("(\:|\-)"); if (hex.length != 6) { throw new IllegalArgumentException("Invalid MAC address."); } try { for (int i = 0; i < 6; i++) { bytes[i] = (byte) Integer.parseInt(hex[i], 16); } } catch (NumberFormatException e) { throw new IllegalArgumentException("Invalid hex digit in MAC address."); } return bytes; }
數據包流向示意圖
區域網
- 魔術包通過手機或者電腦發送;
- 數據包經過路由器廣播到每台機器;
- 被喚醒的電腦收到魔術包並且匹配成功,執行喚醒;
- 經過試驗發現,如果電腦是待機狀態,可以通過主機名或者具體IP如:192.168.1.200喚醒,如果是關機了則沒有辦法,我想應該是待機時候路由器分配給這個電腦的IP地址沒有消失;
公網
- 魔術包通過路由器的域名:http://mypc.router.net 發送到路由器;
- 路由器收到數據,通過埠轉發到相應的IP地址,192.168.1.100和埠9,告訴這台機器可以喚醒,ARP綁定必須存在,這個是因為很多無法遠程喚醒的關鍵所在;看過另一種處理就是轉發地址改成192.168.1.255 子網廣播地址,埠不變也是9,好像就不用ARP綁定,這種方法理論上行得通,我這邊暫時就不試驗,試過的同學可以告訴我;
- 被喚醒的電腦收到魔術包並且匹配成功,執行喚醒;
思考
可以看到區域網和公網喚醒存在差別:
- 區域網被喚醒的IP地址是廣播地址:192.168.1.255,路由器收到後通過廣播,數據包一定可以發送192.168.1.100這台機器;
- 公網喚醒我們無辦法填寫具體的內網地址,只能配置路由器的公網IP,然後通過數據轉發到具體的電腦IP地址,由於不是廣播地址,也由於路由器ARP映射表在電腦關機後一定時間會丟失,所以路由器沒有辦法知道192.168.1.100是MAC所對應那台機器,所以魔術包被丟棄,所以要麼增加ARP綁定,要麼添加埠轉發規則到廣播地址,困擾多年無法遠程喚醒的問題解決。
請關注我的小站
https://tinyx.cc/blog
推薦閱讀:
※「茴」字的五種寫法
※Site to Site IPsec VPN
※Python網路編程中的套接字名和DNS解析。
※計算機網路教程之物理層
※計算機網路(筆記)03--計算機網路結構