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

什麼是網絡喚醒

網絡喚醒(Wake-on-LAN,WOL)是一種計算機局域網喚醒技術,使局域網內處於關機或休眠狀態的計算機,將狀態轉換成引導(Boot Loader)或運行狀態。無線喚醒(Wake-on-Wireless-LAN,WoWLAN)做爲 WOL 的補充技術,使用無線網卡去喚醒計算機。網絡喚醒在通常的局域網環境裏使用有限廣播地址(255.255.255.255)便可,因爲路由器都不轉發目的地址爲有限廣播地址的數據報,所以在複雜網絡狀況下一般使用子網定向廣播地址。在局域網外喚醒局域網內特定計算機,可使用路由器的 DDNS 與端口轉發。git

在1996年10月,英特爾和 IBM 成立了 Advanced Manageability Alliance。1997年4月,聯盟提出了 WOL 技術。這是 WOL 技術的起源,隨後各大廠商紛紛推出了本身的 WOL 技術標準。本文所討論的 WOL 技術是由 AMD 公司提出的 Magic Packet(幻數據包,魔術包)喚醒方式,這裏給出 AMD 關於此技術的白皮書github

幻數據包(Magic Packet)

幻數據包是一個廣播幀,包含目標計算機的MAC地址。因爲 MAC 地址的惟一性,使數據包能夠在網絡中被惟一的識別。幻數據包發送一般使用無鏈接的傳輸協議,如 UDP ,發送端口爲 7 或 9 ,這只是一般作法,沒有限制。c#

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 中。less

FF FF FF FF FF FF FF MAC 地址 × 16 4-6字節的密碼(可空)

例如 MAC 地址爲 11 22 33 44 55 66 的目標計算機,幻數據包的格式爲:async

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-lancode

該項目爲 Xamarin 跨平臺項目,包含 Xamarin.Android 與 UWP 。支持自動掃描添加局域網設備。路由

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

下面給出的是發送幻數據包的方法:

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);
    }
}
相關文章
相關標籤/搜索