11

使用 C# 实现 LAN 唤醒的最佳方式是什么?

LAN 环境中的机器需要该功能(而不是通过 Internet)。该方法需要足够强大以处理防火墙和其他此类问题。此外,对于不支持或禁用此功能的系统,是否有替代方案?

主要目标 - 通过 LAN 唤醒机器(从关机/休眠状态) - 这将使用 C# 进行编程。

请指导。

PS:我遇到了以下情况:

  1. http://blog.memos.cz/index.php/team/2008/06/12/wake-on-lan-in-csharp
  2. http://community.bartdesmet.net/blogs/bart/archive/2006/04/02/3858.aspx
  3. http://www.codeproject.com/KB/IP/cswol.aspx

但是,我对此并不陌生,因此无法确定解决方案是否足够全面。如果有人可以推荐遵循上述任何一篇文章,那会有所帮助。

4

3 回答 3

18

很老的问题,我知道,但仍然有效。由于在接受的答案中没有看到任何 C#,我编写了自己的“Wake On Lan”代码。

我的目标是制作一个通用且简单Wake On Lan class的:

  • 适用于ipv4ipv6dual-stack
  • 适用于连接到不同网络(两台计算机)的一个或多个网卡(NICS)。
  • 可与任何标准十六进制格式的macaddress一起使用。
  • 使用多播工作(使用多个 NIC 时,Windows 中的广播有问题,使用 ipv6 时不支持)。

如何使用:

您所需要的只是您要唤醒的计算机上有线网卡的MAC地址。任何标准的十六进制表示都可以。然后像这样调用代码:

string mac = "01-02-03-04-05-06";
await WOL.WakeOnLan(mac);

这是课程:

using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

public static class WOL
{

    public static async Task WakeOnLan(string macAddress)
    {
        byte[] magicPacket = BuildMagicPacket(macAddress);
        foreach (NetworkInterface networkInterface in NetworkInterface.GetAllNetworkInterfaces().Where((n) =>
            n.NetworkInterfaceType != NetworkInterfaceType.Loopback && n.OperationalStatus == OperationalStatus.Up))
        {
            IPInterfaceProperties iPInterfaceProperties = networkInterface.GetIPProperties();
            foreach (MulticastIPAddressInformation multicastIPAddressInformation in iPInterfaceProperties.MulticastAddresses)
            {
                IPAddress multicastIpAddress = multicastIPAddressInformation.Address;
                if (multicastIpAddress.ToString().StartsWith("ff02::1%", StringComparison.OrdinalIgnoreCase)) // Ipv6: All hosts on LAN (with zone index)
                {
                    UnicastIPAddressInformation unicastIPAddressInformation = iPInterfaceProperties.UnicastAddresses.Where((u) =>
                        u.Address.AddressFamily == AddressFamily.InterNetworkV6 && !u.Address.IsIPv6LinkLocal).FirstOrDefault();
                    if (unicastIPAddressInformation != null)
                    {
                        await SendWakeOnLan(unicastIPAddressInformation.Address, multicastIpAddress, magicPacket);
                        break;
                    }
                }
                else if (multicastIpAddress.ToString().Equals("224.0.0.1")) // Ipv4: All hosts on LAN
                {
                    UnicastIPAddressInformation unicastIPAddressInformation = iPInterfaceProperties.UnicastAddresses.Where((u) =>
                        u.Address.AddressFamily == AddressFamily.InterNetwork && !iPInterfaceProperties.GetIPv4Properties().IsAutomaticPrivateAddressingActive).FirstOrDefault();
                    if (unicastIPAddressInformation != null)
                    {
                        await SendWakeOnLan(unicastIPAddressInformation.Address, multicastIpAddress, magicPacket);
                        break;
                    }
                }
            }
        }
    }

    static byte[] BuildMagicPacket(string macAddress) // MacAddress in any standard HEX format
    {
        macAddress = Regex.Replace(macAddress, "[: -]", "");
        byte[] macBytes = new byte[6];
        for (int i = 0; i < 6; i++)
        {
            macBytes[i] = Convert.ToByte(macAddress.Substring(i * 2, 2), 16);
        }

        using (MemoryStream ms = new MemoryStream())
        {
            using (BinaryWriter bw = new BinaryWriter(ms))
            {
                for (int i = 0; i < 6; i++)  //First 6 times 0xff
                {
                    bw.Write((byte)0xff);
                }
                for (int i = 0; i < 16; i++) // then 16 times MacAddress
                {
                    bw.Write(macBytes);
                }
            }
            return ms.ToArray(); // 102 bytes magic packet
        }
    }

    static async Task SendWakeOnLan(IPAddress localIpAddress, IPAddress multicastIpAddress, byte[] magicPacket)
    {
        using (UdpClient client = new UdpClient(new IPEndPoint(localIpAddress, 0)))
        {
            await client.SendAsync(magicPacket, magicPacket.Length, multicastIpAddress.ToString(), 9);
        }
    }
}

这个怎么运作:

该代码通过枚举所有“启动”并连接到您的网络(通常只有一个)的网卡来工作。它将使用多播向所有连接的网络发送“魔术包”,该多播适用于 ipv4 和 ipv6(不用担心网络泛滥,它只有 102 个字节)。

要工作,您要唤醒的计算机必须具有有线连接(无线计算机无法唤醒,因为它们未连接到任何网络,当它们关闭时)。发送数据包的计算机可以无线连接。

防火墙通常没有问题,因为计算机已关闭,因此防火墙未激活。

您必须确保它'Wake on lan'enabled计算机BIOS和网卡上。

.Net 6 的更新(和错误修复):

修复了一个错误,如果 Ipv6 在发送数据包的计算机上运行,​​但在应该被唤醒的计算机上没有,则它不会尝试 Ipv4(这在上面的代码中已修复)。

这是适用于 .Net 6 的代码(借用了@Oskar Sjôberg 的一些代码) - implicit usings turned on

using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text.RegularExpressions;

public static class WOL
{
    public static async Task WakeOnLan(string macAddress)
    {
        byte[] magicPacket = BuildMagicPacket(macAddress);
        foreach (NetworkInterface networkInterface in NetworkInterface.GetAllNetworkInterfaces().Where((n) =>
            n.NetworkInterfaceType != NetworkInterfaceType.Loopback && n.OperationalStatus == OperationalStatus.Up))
        {
            IPInterfaceProperties iPInterfaceProperties = networkInterface.GetIPProperties();
            foreach (MulticastIPAddressInformation multicastIPAddressInformation in iPInterfaceProperties.MulticastAddresses)
            {
                IPAddress multicastIpAddress = multicastIPAddressInformation.Address;
                if (multicastIpAddress.ToString().StartsWith("ff02::1%", StringComparison.OrdinalIgnoreCase)) // Ipv6: All hosts on LAN (with zone index)
                {
                    UnicastIPAddressInformation? unicastIPAddressInformation = iPInterfaceProperties.UnicastAddresses.Where((u) =>
                        u.Address.AddressFamily == AddressFamily.InterNetworkV6 && !u.Address.IsIPv6LinkLocal).FirstOrDefault();
                    if (unicastIPAddressInformation != null)
                    {
                        await SendWakeOnLan(unicastIPAddressInformation.Address, multicastIpAddress, magicPacket);
                    }
                }
                else if (multicastIpAddress.ToString().Equals("224.0.0.1")) // Ipv4: All hosts on LAN
                {
                    UnicastIPAddressInformation? unicastIPAddressInformation = iPInterfaceProperties.UnicastAddresses.Where((u) =>
                        u.Address.AddressFamily == AddressFamily.InterNetwork && !iPInterfaceProperties.GetIPv4Properties().IsAutomaticPrivateAddressingActive).FirstOrDefault();
                    if (unicastIPAddressInformation != null)
                    {
                        await SendWakeOnLan(unicastIPAddressInformation.Address, multicastIpAddress, magicPacket);
                    }
                }
            }
        }
    }

    static byte[] BuildMagicPacket(string macAddress) // MacAddress in any standard HEX format
    {
        macAddress = Regex.Replace(macAddress, "[: -]", "");
        byte[] macBytes = Convert.FromHexString(macAddress);

        IEnumerable<byte> header = Enumerable.Repeat((byte)0xff, 6); //First 6 times 0xff
        IEnumerable<byte> data = Enumerable.Repeat(macBytes, 16).SelectMany(m => m); // then 16 times MacAddress
        return header.Concat(data).ToArray();
    }

    static async Task SendWakeOnLan(IPAddress localIpAddress, IPAddress multicastIpAddress, byte[] magicPacket)
    {
        using UdpClient client = new(new IPEndPoint(localIpAddress, 0));
        await client.SendAsync(magicPacket, magicPacket.Length, new IPEndPoint(multicastIpAddress, 9));
    }
}
于 2019-09-21T18:30:52.960 回答
15

对于 WOL 问题,您必须澄清三个问题才能使其发挥作用:

  1. 通过以太网电缆发送 WOL
  2. 配置您的 PC 以侦听此类数据包并唤醒
  3. 确保数据包从发送者到接收者(防火墙、网关等)

正如您已经在网上找到的那样,对于用 C# 编程的第一个问题,现有几种解决方案(在浏览了您的链接之后,我将从第一个开始)。

第二个是您只能通过配置网络适配器才能实现的目标。只需打开设备管理器并查看网络适配器的属性,如果存在这样的选项并且您是否可以启用它。这无法编程,因为每个网络适配器都有该功能的另一个实现以及如何启用它。

第三个问题也不能用 C# 解决。这是一个纯粹的网络问题,您必须配置您的路由器、网关、ids 系统等以允许这样的数据包并让它从发送方流向接收方。由于事实上,WOL 数据包始终是广播数据包(dest-ip 255.255.255.255),它不会离开您的本地网络,并且始终会从路由器、网关或任何其他网络之间的网桥(例如 vpns、 ETC。)。

最后但并非最不重要的一点是,我会提醒您,第一个问题可以分为一些较小的数据包,但据我所知,这些问题都被您提供的链接所限制。

于 2009-05-14T07:27:06.923 回答
6

我正在尝试 Poul Bak 的答案,但无法唤醒我的目标计算机。在验证了第三方应用程序WakeMeOnLan实际上能够唤醒我的目标计算机后,我编写了对我有用的代码:

void SendWakeOnLan(PhysicalAddress target)
{   
    var header = Enumerable.Repeat(byte.MaxValue, 6);
    var data = Enumerable.Repeat(target.GetAddressBytes(), 16).SelectMany(mac => mac);

    var magicPacket = header.Concat(data).ToArray();
    
    using var client = new UdpClient();

    client.Send(magicPacket, magicPacket.Length, new IPEndPoint(IPAddress.Broadcast, 9));
}

用法:

只需像这样传入目标计算机的mac地址:

SendWakeOnLan(PhysicalAddress.Parse("0A-0B-0C-0D-0E-0F"));

我认为这个答案和 Poul Bak 的答案之间的主要区别在于,这段代码使用 IPv4 上的广播而不是 IPv4/IPv6 上的多播,也许我的网络设备没有处理/设置正确地进行多播。

于 2021-01-01T12:48:03.853 回答