1. 程式人生 > >java實現遠端喚醒一臺計算機

java實現遠端喚醒一臺計算機

1. 在BIOS設定支援網路喚醒

大多數整合網絡卡都能實現網路喚醒功能,不過需要事先進入BIOS中開啟網路喚醒功能,不同主機板的設定不一樣,以VIA 主機板為例,在BIOS中找到“OnBoard LAN”選項,將它設成“Enabled”。同時將“POWER MANAGEMENT SETUP(電源管理設定)”下的“Power On by LAN/Ring”選項設為“Enabled”,最後將“Wake On LAN(網路喚醒)”選項設定為“Enabled”,設定好後儲存退出。

不同系統可能還需要額外的操作才能保證網路喚醒的可用性,以win10系統為例:

開啟裝置管理器,進入網路介面卡中自己網絡卡的屬性設定,把相關的服務都啟用了。

2. 網路喚醒的必備條件

  • 網路喚醒需要終端的主機板和網絡卡支援,需要先在BIOS設定支援網路喚醒
  • 網路喚醒要接通電源保證網絡卡能通電 要接網線 不能是wifi
  • 如果強制關機 可能不能通過網路喚醒來開機
  • 跨交換機或者跨路由的話就有可能不支援喚醒
  • 跨多層交換機的話即使ping通也未必能喚醒
  • 在同一網段下進行網路喚醒最為省事

3. 網路喚醒原理

這裡提到一個魔術包Magic Packet的概念,魔術包指AMD公司開發的喚醒資料包,其實是一種特定的資料格式。將喚醒魔術包傳送的被喚醒機器的網絡卡上,具有遠端喚醒的網絡卡都支援這個標準,用16進製表示。

假設你的網絡卡實體地址為00:15:17:53:d4:f9, 這段Magic Packet內容如下:

    FFFFFFFFFFFF00151753d4f900151753d4f900151753d4f900151753d4f9
    00151753d4f900151753d4f900151753d4f900151753d4f900151753d4f9
    00151753d4f900151753d4f900151753d4f900151753d4f900151753d4f9
    00151753d4f900151753d4f9

這段資料轉化為二進位制的資料,通過socket技術傳送資料包以及目的mac和目的廣播地址,就會喚醒目的網絡卡,從而喚醒主機。

資料包流向圖:

當資料包被廣播到192.168.1網段之後,根據資料攜帶的mac資訊匹配到具體的主機。

4. 廣播地址

這裡主要講解廣播地址的概念和計算。

所謂廣播地址指同時向網上所有的主機發送報文。

對一個既定的ip來說,其網路地址就是主機位全換成0,廣播地址就是主機位是全換成1

例子:先把子網掩碼化成二進位制,再對應的把子網掩碼後面是0的部分對著Ip地址換成0和1就是網路地址和主機地址,比如

192.168.1.3 (地址)/255.255.255.252(掩碼) ,換算一下成二進位制

11111111.11111111.11111111.111111 00 /掩碼

11000000.10101000.00000001.000000 11 /地址

掩碼後兩位是0,那麼把地址的後兩位換成0就是網路地址,換成1就是廣播地址

那麼就是:11000000.10101000.00000001.000000 00

11000000.10101000.00000001.000000 11

把上面的二進位制轉換成10進位制

得到192.168.1.0是網路地址,192.168.1.3是廣播地址

5. java程式碼-網路喚醒

先計算被喚醒主機的廣播地址

   //根據子網掩碼和ip得到主機的廣播地址
    public static String getBroadcastAddress(String ip, String subnetMask){
        String ipBinary = toBinary(ip);
        String subnetBinary = toBinary(subnetMask);
        String broadcastBinary = getBroadcastBinary(ipBinary, subnetBinary);
        String wholeBroadcastBinary=spiltBinary(broadcastBinary);
        return binaryToDecimal(wholeBroadcastBinary);
    }

    //二進位制的ip字串轉十進位制
    private static String binaryToDecimal(String wholeBroadcastBinary){
        String[] strings = wholeBroadcastBinary.split("\\.");
        StringBuilder sb = new StringBuilder(40);
        for (int j = 0; j < strings.length ; j++) {
            String s = Integer.valueOf(strings[j], 2).toString();
            sb.append(s).append(".");
        }
        return sb.toString().substring(0,sb.length()-1);
    }

    //按8位分割二進位制字串
    private static String spiltBinary(String broadcastBinary){
        StringBuilder stringBuilder = new StringBuilder(40);
        char[] chars = broadcastBinary.toCharArray();
        int count=0;
        for (int j = 0; j < chars.length; j++) {
            if (count==8){
                stringBuilder.append(".");
                count=0;
            }
            stringBuilder.append(chars[j]);
            count++;
        }
        return stringBuilder.toString();
    }

    //得到廣播地址的二進位制碼
    private static String getBroadcastBinary(String ipBinary, String subnetBinary){
        int i = subnetBinary.lastIndexOf('1');
        String broadcastIPBinary = ipBinary.substring(0,i+1);
        for (int j = broadcastIPBinary.length(); j < 32 ; j++) {
            broadcastIPBinary=broadcastIPBinary+"1";
        }
        return broadcastIPBinary;
    }

    //轉二進位制
    private static String toBinary(String content){
        String binaryString="";
        String[] ipSplit = content.split("\\.");
        for ( String split : ipSplit ) {
            String s = Integer.toBinaryString(Integer.valueOf(split));
            int length = s.length();
            for (int i = length; i <8 ; i++) {
                s="0"+s;
            }
            binaryString = binaryString +s;
        }
        return binaryString;
    }

執行網路喚醒

    /**
     * 喚醒主機
     * @param ip         主機ip
     * @param mac     主機mac
     * @param subnetMask      主機子網掩碼
     */
    publicstaticvoidwakeUpDevice(String ip,String mac,String subnetMask){
        ip=ip.trim();
        mac=mac.trim();
        subnetMask=subnetMask.trim();
        String broadcastAddress=getBroadcastAddress(ip,subnetMask);
        mac = mac.replace("-", "");
        wakeBy(broadcastAddress,mac,389);
    }

    /**
     *   網路喚醒
     * @param ip            主機ip
     * @param mac        主機mac
     * @param port        埠
     */
    privatestaticvoidwakeBy(String ip, String mac, int port){
        //構建magic魔術包
        String MagicPacage = "FFFFFFFFFFFF";
        for (int i = 0; i < 16; i++) {
            MagicPacage += mac;
        }
        byte[] MPBinary = hexStr2BinArr(MagicPacage);
        try {
            InetAddress address = InetAddress.getByName(ip);
            DatagramSocket socket = new DatagramSocket(port);
            DatagramPacket packet = new DatagramPacket(MPBinary, MPBinary.length, address, port);
            //傳送udp資料包到廣播地址
            socket.send(packet);
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static byte[] hexStr2BinArr(String hexString) {
        String hexStr = "0123456789ABCDEF";
        int len = hexString.length() / 2;
        byte[] bytes = new byte[len];
        byte high = 0;
        byte low = 0;
        for (int i = 0; i < len; i++) {
            high = (byte) ((hexStr.indexOf(hexString.charAt(2 * i))) << 4);
            low = (byte) hexStr.indexOf(hexString.charAt(2 * i + 1));
            bytes[i] = (byte) (high | low);
        }
        return bytes;
    }

注意:當跨網段進行喚醒時,即發起喚醒的地址和被喚醒的目的地址不在同一個網段,是否需要做一些調整取決於你的網路配置。我這邊的情況是,比如當50網段的伺服器傳送網路喚醒魔術包到62網段,是行不通的,需要在62閘道器下增加ip轉發廣播ip forward-broadcast。

轉自 https://my.oschina.net/u/3490860/blog/1613093