Java通过WOL,3步实现远程开机

川长思鸟来 2023-01-05 12:57 390阅读 0赞

目录

远程开机(Wake onLAN)

WOL实现远程开机分为3大步。

魔术包Magic Packet

第一步:设置主机的有线网卡Wake on Magic Package属性为Enable。

1.首先进入cmd命令窗口,查看自己的有线网卡的ip地址和mac地址,写java程序时需要使用。

2.网卡要开启唤醒魔包(Wake on magic packet)

第二步:进入BIOS,设置Wake On Lan属性为Enable

  1. 第三步:远程端启动java程序,发送MagicPacket(魔术包)即可唤醒。

问题排查

  1. 没有Wake on magic packet或唤醒魔包

在线更新

驱动包更新

  1. 在路由器环境下,想在公网实现对内网电脑开机

  2. 跨网段进行唤醒


远程开机(Wake onLAN)

远程开机也被称为远程唤醒技术(Wake on Lan: WOL),是指可以通过局域网、互联网或者通讯网实现远程开机,无论目标主机离用户有多远、处于什么位置,只要其与发送命令主机可以通信,就能够被随时启动,该技术被现在的大多数主板与网卡所支持。

远程开机的实现主要依靠向目标主机发送特定格式的数据包,最初AMD公司推出的MagicPackage用于生成远程唤醒所需的特殊数据包,俗称魔术包(Magic Package)。MagicPackage技术只是AMD公司开发并推广的技术,尚未成为一项国际标准,但是该技术受到大多数网卡制造商的支持,因此具有远程唤醒功能的网卡都兼容这项技术。

WOL实现远程开机分为3大步。

第一步,设置主机的有线网卡Wake on Magic Package属性为Enable。

第二步,进入BIOS,设置Wake On Lan属性为Enable。

第三步,远程端启动java程序,发送MagicPacket(魔术包)即可唤醒。

魔术包Magic Packet

这里提到一个魔术包Magic Packet的概念,魔术包指AMD公司开发的唤醒数据包,其实是一种特定的数据格式。将唤醒魔术包发送的被唤醒机器的网卡上,具有远程唤醒的网卡都支持这个标准,用16进制表示。

魔术包是用16进制表示的数据包,它由固定的前缀数据以及固定重复次数的目标主机MAC地址所组成。

所谓固定前缀数据即6对“FF”,所谓固定重复次数即16次,也就是说魔术包是由12个“F”加重复16次的主机MAC地址组成,例如本文所用试验机的MAC地址为“66-00-6A-8F-1D-64”,

所以使该机远程开机的魔术包为:

String magicPacageData = “FFFFFFFFFFFF” +
“64006A8F1D64” + “64006A8F1D64” + “64006A8F1D64” + “64006A8F1D64” +
“64006A8F1D64” + “64006A8F1D64” + “64006A8F1D64” + “64006A8F1D64” +
“64006A8F1D64” + “64006A8F1D64” + “64006A8F1D64” + “64006A8F1D64” +
“64006A8F1D64” + “64006A8F1D64” + “64006A8F1D64” + “64006A8F1D64”;

  1. Windows系统中,主机的MAC地址可以通过在命令窗口中输入“ipconfig -all”命令查看。

这段数据转化为二进制的数据,通过socket技术发送数据包以及目的mac和目的广播地址,就会唤醒目的网卡,从而唤醒主机。

第一步:设置主机的有线网卡Wake on Magic Package属性为Enable。

1.首先进入cmd命令窗口,查看自己的有线网卡的ip地址和mac地址,写java程序时需要使用。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZseTkxMDkwNQ_size_16_color_FFFFFF_t_70

2.网卡要开启唤醒魔包(Wake on magic packet)

需要找到有线网卡右键点击属性找到高级将Wake on magic packet属性的值改为Enabled。找到网卡的方式有很多。

第一种:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZseTkxMDkwNQ_size_16_color_FFFFFF_t_70 1

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZseTkxMDkwNQ_size_16_color_FFFFFF_t_70 2

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZseTkxMDkwNQ_size_16_color_FFFFFF_t_70 3

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZseTkxMDkwNQ_size_16_color_FFFFFF_t_70 4

如果为中文参考下图

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZseTkxMDkwNQ_size_16_color_FFFFFF_t_70 5

以上以设置好第一步,可以开始第二步。

下面提供一些其他找到有线网卡属性的方式

第二种

键盘点击windwos或鼠标点击左下角搜索设备管理器—win10电脑自带就有搜索框

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZseTkxMDkwNQ_size_16_color_FFFFFF_t_70 6

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZseTkxMDkwNQ_size_16_color_FFFFFF_t_70 7

第三种

找到更改适配器之后选择有线网卡,具体步骤参看下图

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZseTkxMDkwNQ_size_16_color_FFFFFF_t_70 8

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZseTkxMDkwNQ_size_16_color_FFFFFF_t_70 9

第一步已经全部结束。


第二步:进入BIOS,设置Wake On Lan属性为Enable

配置需要进入BIOS界面。

电脑重启,如果是DELL的主板,开机时一直按F12,不同的主板进入BIOS的操作不同,可以到网上搜索。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZseTkxMDkwNQ_size_16_color_FFFFFF_t_70 10

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZseTkxMDkwNQ_size_16_color_FFFFFF_t_70 11

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZseTkxMDkwNQ_size_16_color_FFFFFF_t_70 12

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZseTkxMDkwNQ_size_16_color_FFFFFF_t_70 13

另一种BIOS界面

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZseTkxMDkwNQ_size_16_color_FFFFFF_t_70 14

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZseTkxMDkwNQ_size_16_color_FFFFFF_t_70 15

第三步:远程端启动java程序,发送MagicPacket(魔术包)即可唤醒。

  1. import lombok.extern.slf4j.Slf4j;
  2. import org.apache.commons.lang3.StringUtils;
  3. import java.io.IOException;
  4. import java.net.DatagramPacket;
  5. import java.net.InetAddress;
  6. import java.net.MulticastSocket;
  7. import java.net.UnknownHostException;
  8. import java.nio.ByteBuffer;
  9. import java.util.Collections;
  10. /**
  11. * @Title: 发送魔术包:实现电脑远程开机(WOL)
  12. * @ClassName: com.devicemag.core.utils.MagicPackageUtils.java
  13. * @Description:
  14. *
  15. * @Copyright 2020-2021 - Powered By 研发中心
  16. * @author: FLY
  17. * @date: 2021/1/8 9:32
  18. * @version V1.0
  19. */
  20. @Slf4j
  21. public class MagicPackageUtils {
  22. /**
  23. * main方法,发送UDP广播,实现远程开机,目标计算机的MAC地址为:D8-9E-F3-95-AC-74
  24. */
  25. public static void main(String[] args) {
  26. // 10.0.50.138 D8-9E-F3-88-B6-3C
  27. // 10.0.50.129 66-00-6A-8F-1D-64
  28. // 10.0.50.186 D8-9E-F3-95-AC-74
  29. // 单播
  30. // sendMagicPackage("10.0.50.186", "D8-9E-F3-95-AC-74");
  31. // 广播,需要先根据子网掩码和ip得到主机的广播地址
  32. String broadcastAddress=getBroadcastAddress("10.0.50.186","255.255.255.0");
  33. System.out.println(broadcastAddress);
  34. }
  35. /**
  36. * @Title: 组装魔术包数据
  37. * @MethodName: assembleMagicData
  38. * @param mac
  39. * @Return java.lang.String
  40. * @Exception
  41. * @Description:
  42. *
  43. * 魔术包是用16进制表示的数据包,它由固定的前缀数据以及固定重复次数的目标主机MAC地址所组成。
  44. * 所谓固定前缀数据即6对“FF”,所谓固定重复次数即16次,也就是说魔术包是由12个“F”加重复16次的主机MAC地址组成,例如本文所用试验机的MAC地址为“66-00-6A-8F-1D-64”,
  45. * 所以使该机远程开机的魔术包为:
  46. * String magicPacageData = "FFFFFFFFFFFF" +
  47. * "64006A8F1D64" + "64006A8F1D64" + "64006A8F1D64" + "64006A8F1D64" +
  48. * "64006A8F1D64" + "64006A8F1D64" + "64006A8F1D64" + "64006A8F1D64" +
  49. * "64006A8F1D64" + "64006A8F1D64" + "64006A8F1D64" + "64006A8F1D64" +
  50. * "64006A8F1D64" + "64006A8F1D64" + "64006A8F1D64" + "64006A8F1D64";
  51. *
  52. * 在Windows系统中,主机的MAC地址可以通过在命令窗口中输入“ipconfig -all”命令查看。
  53. *
  54. * @author: FLY
  55. * @date: 2021/1/11 18:02
  56. */
  57. private static String assembleMagicData(String mac) {
  58. String macR = null;
  59. if (mac.contains("-")) {
  60. macR = mac.replaceAll("-", "");
  61. } else if (mac.contains(":")) {
  62. macR = mac.replaceAll(":", "");
  63. }
  64. String repeatedStr = createRepeatedStr(macR, 16);
  65. String magicData = new StringBuilder("FFFFFFFFFFFF").append(repeatedStr).toString();
  66. return magicData;
  67. }
  68. /**
  69. * @Title: 远程开机
  70. * @MethodName: sendMagicPackage
  71. * @param ip
  72. * @param mac
  73. * @Return void
  74. * @Exception
  75. * @Description: 发送UDP广播,实现远程开机
  76. *
  77. * @author: FLY
  78. * @date: 2021/1/11 17:19
  79. */
  80. public static void sendMagicPackage(String ip, String mac) {
  81. log.info("【魔包远程开机】,IP地址:{},MAC地址:{}", ip, mac);
  82. if (StringUtils.isBlank(ip)
  83. || StringUtils.isBlank(mac)) {
  84. return;
  85. }
  86. //端口号
  87. int port = 9;
  88. //魔术包数据
  89. /*String magicPacageData = "FFFFFFFFFFFF" +
  90. "64006A8F1D64" + "64006A8F1D64" + "64006A8F1D64" + "64006A8F1D64" +
  91. "64006A8F1D64" + "64006A8F1D64" + "64006A8F1D64" + "64006A8F1D64" +
  92. "64006A8F1D64" + "64006A8F1D64" + "64006A8F1D64" + "64006A8F1D64" +
  93. "64006A8F1D64" + "64006A8F1D64" + "64006A8F1D64" + "64006A8F1D64";*/
  94. String magicPacageData = assembleMagicData(mac);
  95. log.info("【魔包远程开机】,IP地址:{},MAC地址:{},魔术包数据:{}", ip, mac, magicPacageData);
  96. // mac地址转换为2进制的魔术包数据
  97. byte[] command = hexStr2Byte(magicPacageData);
  98. //广播魔术包
  99. try {
  100. //1.获取ip地址
  101. InetAddress address = InetAddress.getByName(ip);
  102. //2.获取广播socket
  103. MulticastSocket socket = new MulticastSocket(port);
  104. //3.封装数据包
  105. /*public DatagramPacket(byte[] buf,int length
  106. * ,InetAddress address
  107. * ,int port)
  108. * buf:缓存的命令
  109. * length:每次发送的数据字节数,该值必须小于等于buf的大小
  110. * address:广播地址
  111. * port:广播端口
  112. */
  113. DatagramPacket packet = new DatagramPacket(command, command.length, address, port);
  114. //4.发送数据
  115. socket.send(packet);
  116. //5.关闭socket
  117. socket.close();
  118. } catch (UnknownHostException e) {
  119. //Ip地址错误时候抛出的异常
  120. log.error("【魔包远程开机】异常-Ip地址错误,IP地址:{},MAC地址:{},异常信息:{}", ip, mac, e);
  121. } catch (IOException e) {
  122. //获取socket失败时候抛出的异常
  123. log.error("【魔包远程开机】异常-获取socket失败,IP地址:{},MAC地址:{},异常信息:{}", ip, mac, e);
  124. }
  125. }
  126. /**
  127. * @Title: 将16进制字符串转换为用byte数组表示的二进制形式
  128. * @MethodName: hexStr2Byte
  129. * @param hex
  130. * @Return byte[]
  131. * @Exception
  132. * @Description:
  133. *
  134. * @author: FLY
  135. * @date: 2021/1/11 17:22
  136. */
  137. private static byte[] hexStr2Byte(String hex) {
  138. ByteBuffer bf = ByteBuffer.allocate(hex.length() / 2);
  139. for (int i = 0; i < hex.length(); i++) {
  140. String hexStr = String.valueOf(hex.charAt(i));
  141. i++;
  142. hexStr += hex.charAt(i);
  143. byte b = (byte) Integer.parseInt(hexStr, 16);
  144. bf.put(b);
  145. }
  146. return bf.array();
  147. }
  148. /**
  149. * @Title: 将一个字符串重复n次
  150. * @MethodName: createRepeatedStr
  151. * @param seed
  152. * @param n
  153. * @Return java.lang.String
  154. * @Exception
  155. * @Description:
  156. *
  157. * @author: FLY
  158. * @date: 2021/1/11 17:22
  159. */
  160. private static String createRepeatedStr(String seed, int n) {
  161. return String.join("", Collections.nCopies(n, seed));
  162. }
  163. /**
  164. * @Title: 根据子网掩码和ip得到主机的广播地址
  165. * @MethodName: getBroadcastAddress
  166. * @param ip
  167. * @param subnetMask 子网掩码
  168. * @Return java.lang.String
  169. * @Exception
  170. * @Description:
  171. *
  172. * @author: FLY
  173. * @date: 2021/1/12 19:02
  174. */
  175. public static String getBroadcastAddress(String ip, String subnetMask){
  176. String ipBinary = toBinary(ip);
  177. String subnetBinary = toBinary(subnetMask);
  178. String broadcastBinary = getBroadcastBinary(ipBinary, subnetBinary);
  179. String wholeBroadcastBinary=spiltBinary(broadcastBinary);
  180. return binaryToDecimal(wholeBroadcastBinary);
  181. }
  182. /**
  183. * @Title: 二进制的ip字符串转十进制
  184. * @MethodName: binaryToDecimal
  185. * @param wholeBroadcastBinary
  186. * @Return java.lang.String
  187. * @Exception
  188. * @Description:
  189. *
  190. * @author: FLY
  191. * @date: 2021/1/12 19:03
  192. */
  193. private static String binaryToDecimal(String wholeBroadcastBinary){
  194. String[] strings = wholeBroadcastBinary.split("\\.");
  195. StringBuilder sb = new StringBuilder(40);
  196. for (int j = 0; j < strings.length ; j++) {
  197. String s = Integer.valueOf(strings[j], 2).toString();
  198. sb.append(s).append(".");
  199. }
  200. return sb.toString().substring(0,sb.length()-1);
  201. }
  202. /**
  203. * @Title: 按8位分割二进制字符串
  204. * @MethodName: spiltBinary
  205. * @param broadcastBinary
  206. * @Return java.lang.String
  207. * @Exception
  208. * @Description:
  209. *
  210. * @author: FLY
  211. * @date: 2021/1/12 19:03
  212. */
  213. private static String spiltBinary(String broadcastBinary){
  214. StringBuilder stringBuilder = new StringBuilder(40);
  215. char[] chars = broadcastBinary.toCharArray();
  216. int count=0;
  217. for (int j = 0; j < chars.length; j++) {
  218. if (count==8){
  219. stringBuilder.append(".");
  220. count=0;
  221. }
  222. stringBuilder.append(chars[j]);
  223. count++;
  224. }
  225. return stringBuilder.toString();
  226. }
  227. //得到广播地址的二进制码
  228. private static String getBroadcastBinary(String ipBinary, String subnetBinary){
  229. int i = subnetBinary.lastIndexOf('1');
  230. String broadcastIPBinary = ipBinary.substring(0,i+1);
  231. for (int j = broadcastIPBinary.length(); j < 32 ; j++) {
  232. broadcastIPBinary=broadcastIPBinary+"1";
  233. }
  234. return broadcastIPBinary;
  235. }
  236. //转二进制
  237. private static String toBinary(String content){
  238. String binaryString="";
  239. String[] ipSplit = content.split("\\.");
  240. for ( String split : ipSplit ) {
  241. String s = Integer.toBinaryString(Integer.valueOf(split));
  242. int length = s.length();
  243. for (int i = length; i <8 ; i++) {
  244. s="0"+s;
  245. }
  246. binaryString = binaryString +s;
  247. }
  248. return binaryString;
  249. }
  250. }

问题排查

1. 没有Wake on magic packet或唤醒魔包

步骤1中遇到问题为选择高级之后,发现属性里没有Wake on magic packet或唤醒魔包,此处需要的操作是更新网卡的驱动。

更新网卡的驱动有两种,一种是在线更新,前提为连接互联网,操作也相对简单

另一种是驱动包安装,前提为需要到网上下载好有线网卡对应的驱动包然后本地安装。

在线更新

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZseTkxMDkwNQ_size_16_color_FFFFFF_t_70 16

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZseTkxMDkwNQ_size_16_color_FFFFFF_t_70 17

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZseTkxMDkwNQ_size_16_color_FFFFFF_t_70 18

在线更新页面如图,不需要多余的操作。

如果电脑上有驱动精灵之类的软件,在这类软件里也是可以更新驱动的,前提为连接互联网。

驱动包更新

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZseTkxMDkwNQ_size_16_color_FFFFFF_t_70 19

需要选择有线网卡驱动包的位置。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZseTkxMDkwNQ_size_16_color_FFFFFF_t_70 20

有线网卡驱动包可以在网上下载,也可以在网卡制造商的官网下载,需要首先确定网卡型号

2. 在路由器环境下,想在公网实现对内网电脑开机

需要设置路由器的ip映射,将外网地址映射为内网地址,比如tp-link的dmz主机设置.
将目标主机(mac:01-12-43-44-D5-56)的ip地址设置为静态ip,比如192.168.0.99, 然后在路由器也绑定mac和ip

3. 跨网段进行唤醒

注意:当跨网段进行唤醒时,即发起唤醒的地址和被唤醒的目的地址不在同一个网段,是否需要做一些调整取决于你的网络配置。

我这边的情况是,比如当50网段的服务器发送网络唤醒魔术包到62网段,是行不通的,需要在62网关下增加ip转发广播ip forward-broadcast。

发表评论

表情:
评论列表 (有 0 条评论,390人围观)

还没有评论,来说两句吧...

相关阅读