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