1. 程式人生 > >網路喚醒(WOL)全解指南:原理篇

網路喚醒(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. 需要硬體進行支援

建立幻資料包

該專案為 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);
    }
}