天天看點

網絡喚醒(WOL)全解指南:原理篇

什麼是網絡喚醒

網絡喚醒(Wake-on-LAN,WOL)是一種計算機區域網路喚醒技術,使區域網路内處于關機或休眠狀态的計算機,将狀态轉換成引導(Boot Loader)或運作狀态。無線喚醒(Wake-on-Wireless-LAN,WoWLAN)作為 WOL 的補充技術,使用無線網卡去喚醒計算機。網絡喚醒在一般的區域網路環境裡使用有限廣播位址(255.255.255.255)即可,由于路由器都不轉發目的位址為有限廣播位址的資料報,是以在複雜網絡情況下通常使用子網定向廣播位址。在區域網路外喚醒區域網路内特定計算機,可以使用路由器的 DDNS 與端口轉發。

在1996年10月,英特爾和 IBM 成立了 Advanced Manageability Alliance。1997年4月,聯盟提出了 WOL 技術。這是 WOL 技術的起源,随後各大廠商紛紛推出了自己的 WOL 技術标準。本文所讨論的 WOL 技術是由 AMD 公司提出的 Magic Packet(幻資料包,魔術包)喚醒方式,這裡給出 AMD 關于此技術的白皮書。

幻資料包(Magic Packet)

幻資料包是一個廣播幀,包含目标計算機的MAC位址。由于 MAC 位址的唯一性,使資料包可以在網絡中被唯一的識别。幻資料包發送通常使用無連接配接的傳輸協定,如 UDP ,發送端口為 7 或 9 ,這隻是通常做法,沒有限制。

WOL 技術被提出了将近20年,絕大多數的現代網卡都支援在超低功耗下監聽特定的封包,如 ARP。如果裝置網卡接收到一個與自己 MAC 位址相同的幻資料包,則網卡會向計算機的電源或主機闆發出信号以喚醒計算機。大部分的幻資料包在資料鍊路層(OSI模型第2層)上發送,當發送時,使用廣播位址廣播到給定的網絡上,不使用IP位址(OSI模型第3層)。當然這是絕大部分情況,幻資料包也可以使用特定的 IP 位址進行發送。

幻資料包最簡單的構成是6位元組的255(FF FF FF FF FF FF FF),緊接着為目标計算機的48位MAC位址,重複16次,資料包共計102位元組。有時資料包内還會緊接着4-6位元組的密碼資訊。這個幀片段可以包含在任何協定中,最常見的是包含在 UDP 中。

FF FF FF FF FF FF FF MAC 位址 × 16 4-6位元組的密碼(可空)

例如 MAC 位址為 11 22 33 44 55 66 的目标計算機,幻資料包的格式為:

FFFFFFFFFFFF 112233445566 112233445566 112233445566 112233445566 112233445566 112233445566 112233445566 112233445566 112233445566 112233445566 112233445566 112233445566 112233445566 112233445566 112233445566 112233445566 [ABABABABABAB(這裡為6個位元組的密碼)]
           

幻資料包還有一些基本限制條件:

  1. 需要知道目标計算機 MAC 位址
  2. 不提供送達确認
  3. 可能無法在區域網路之外工作
  4. 需要硬體進行支援

建立幻資料包

項目位址:https://github.com/ZhangGaoxing/wake-on-lan

該項目為 Xamarin 跨平台項目,包含 Xamarin.Android 與 UWP 。支援自動掃描添加區域網路裝置。

關于 MAC 位址的掃描擷取,這裡隻說一下思路,詳細請查閱代碼。第一種方式,也是我最開始想到的方式,使用 Ping 來 Ping 整個網段。開了四個線程,1-255大概需要30多秒,稍微有點慢,而且 .NET 的 Ping 類在 Android 上無法限制秒數。第二種方式,百度到的,直接向整個網段發送 UDP 消息,2秒解決戰鬥。掃描完成後擷取 ARP 表就行。

下面給出的是發送幻資料包的方法:

public static async void Wake(string broadcast, int port, byte[] mac)
{
    using (UdpClient udp = new UdpClient())
    {
        udp.EnableBroadcast = true;

        byte[] packet = new byte[6 + 16 * 6];

        for (int i = 0; i < 6; i++)
        {
            packet[i] = 0xFF;
        }

        for (int i = 0; i < 16; i++)
        {
            for (int j = 0; j < 6; j++)
            {
                packet[6 + i * 6 + j] = mac[j];
            }
        }

        await udp.SendAsync(packet, packet.Length, broadcast, port);
    }
}
           

繼續閱讀