在C#中从IP获取本地网络上机器的MAC地址

16

我正在尝试编写一个函数,它以单个IP地址作为参数,并查询本地网络上的机器获取其MAC地址

我看过许多例子,这些例子获取本地机器自己的MAC地址,但是没有发现(我找到的)可以查询本地网络机器的例子。

我知道这样的任务是可行的,因为这个Wake on LAN scanner软件扫描本地IP范围并返回所有打开机器的MAC地址/主机名。

有人能告诉我如何开始尝试使用C#编写此函数吗?任何帮助将不胜感激。谢谢

编辑:

根据Marco Mp下面的评论,已经使用ARP表。 arp类


不确定是否有效,但是通过快速的谷歌搜索,我找到了这个库,应该可以解决问题:http://www.tamirgal.com/blog/post/ARP-Resolver-C-Class.aspx - Marco Mp
谢谢,我相信我已经读到ARP表不一致的情况,想知道是否有一种方法可以“ping” MAC地址。 - Gga
2
我认为如果您进行常规ping(或尝试联系)IP地址,它将导致ARP表刷新(否则网络堆栈将无法首先联系到该机器);当然,这仅在所需的机器在线时才有效(如果有的话)。我不认为您可以获得离线IP地址的可靠结果,特别是如果您具有动态分配的IP地址。虽然我不是网络专家,但我可能是错误的(试图与您一起思考问题)。 - Marco Mp
谢谢,ARP表是正确的选择。在第一条评论中的示例有些困难,因此我已经发布了另一种选择。干杯 - Gga
尝试这个简洁而优美的解决方案: https://dev59.com/pnVC5IYBdhLWcg3wxEN1#37155004 - Dominic Jonas
可能是重复的问题,参考如何使用C#获取IP地址的物理(MAC)地址? - Wai Ha Lee
6个回答

23
public string GetMacAddress(string ipAddress)
{
    string macAddress = string.Empty;
    System.Diagnostics.Process pProcess = new System.Diagnostics.Process();
    pProcess.StartInfo.FileName = "arp";
    pProcess.StartInfo.Arguments = "-a " + ipAddress;
    pProcess.StartInfo.UseShellExecute = false;
    pProcess.StartInfo.RedirectStandardOutput = true;
      pProcess.StartInfo.CreateNoWindow = true;
    pProcess.Start();
    string strOutput = pProcess.StandardOutput.ReadToEnd();
    string[] substrings = strOutput.Split('-');
    if (substrings.Length >= 8)
    {
       macAddress = substrings[3].Substring(Math.Max(0, substrings[3].Length - 2)) 
                + "-" + substrings[4] + "-" + substrings[5] + "-" + substrings[6] 
                + "-" + substrings[7] + "-" 
                + substrings[8].Substring(0, 2);
        return macAddress;
    }

    else
    {
        return "not found";
    }
}

非常晚的编辑: 在开源项目 iSpy (https://github.com/ispysoftware/iSpy) 中,他们使用了这段代码,看起来更好一些。

  public static void RefreshARP()
        {
            _arpList = new Dictionary<string, string>();
            _arpList.Clear();
            try
            {
                var arpStream = ExecuteCommandLine("arp", "-a");
                // Consume first three lines
                for (int i = 0; i < 3; i++)
                {
                    arpStream.ReadLine();
                }
                // Read entries
                while (!arpStream.EndOfStream)
                {
                    var line = arpStream.ReadLine();
                    if (line != null)
                    {
                        line = line.Trim();
                        while (line.Contains("  "))
                        {
                            line = line.Replace("  ", " ");
                        }
                        var parts = line.Trim().Split(' ');

                        if (parts.Length == 3)
                        {
                            string ip = parts[0];
                            string mac = parts[1];
                            if (!_arpList.ContainsKey(ip))
                                _arpList.Add(ip, mac);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.LogExceptionToFile(ex, "ARP Table");
            }
            if (_arpList.Count > 0)
            {
                foreach (var nd in List)
                {
                    string mac;
                    ARPList.TryGetValue(nd.IPAddress.ToString(), out mac);
                    nd.MAC = mac;    
                }
            }
        }

https://github.com/ispysoftware/iSpy/blob/master/Server/NetworkDeviceList.cs

更新2,虽然更晚了,但我认为它是最好的,因为它使用正则表达式来更好地检查精确匹配。

public string getMacByIp(string ip)
{
    var macIpPairs = GetAllMacAddressesAndIppairs();
    int index = macIpPairs.FindIndex(x => x.IpAddress == ip);
    if (index >= 0)
    {
        return macIpPairs[index].MacAddress.ToUpper();
    }
    else
    {
        return null;
    }
}

public List<MacIpPair> GetAllMacAddressesAndIppairs()
{
    List<MacIpPair> mip = new List<MacIpPair>();
    System.Diagnostics.Process pProcess = new System.Diagnostics.Process();
    pProcess.StartInfo.FileName = "arp";
    pProcess.StartInfo.Arguments = "-a ";
    pProcess.StartInfo.UseShellExecute = false;
    pProcess.StartInfo.RedirectStandardOutput = true;
    pProcess.StartInfo.CreateNoWindow = true;
    pProcess.Start();
    string cmdOutput = pProcess.StandardOutput.ReadToEnd();
    string pattern = @"(?<ip>([0-9]{1,3}\.?){4})\s*(?<mac>([a-f0-9]{2}-?){6})";

    foreach (Match m in Regex.Matches(cmdOutput, pattern, RegexOptions.IgnoreCase))
    {
        mip.Add(new MacIpPair()
        {
            MacAddress = m.Groups["mac"].Value,
            IpAddress = m.Groups["ip"].Value
        });
    }

    return mip;
}
public struct MacIpPair
{
    public string MacAddress;
    public string IpAddress;
}

2
这是正确的答案,而不是@Macro Mp的回答......为什么不选择这个作为答案呢? - Khan
为什么它不能显示自己的电脑MAC地址,却能找到其他所有设备的呢? - Khan
1
@Khan 可能是因为没有必要在 ARP 表中存储自己的 IP,因为你已经知道自己的 IP 和 MAC。 - online Thomas
https://dev59.com/x3RA5IYBdhLWcg3wuAYo - online Thomas
1
我理解,但是这并不适用于包含[a-f0-9]{2}表达式的情况,这就是为什么我将其更改为[a-f0-9]{1,2}的原因。 - ulmer-morozov
显示剩余6条评论

11
using System.Net;
using System.Runtime.InteropServices;

[DllImport("iphlpapi.dll", ExactSpelling = true)]
public static extern int SendARP(int DestIP, int SrcIP, [Out] byte[] pMacAddr, ref int PhyAddrLen);

try
{
    IPAddress hostIPAddress = IPAddress.Parse("XXX.XXX.XXX.XX");
    byte[] ab = new byte[6];
    int len = ab.Length, 
        r = SendARP((int)hostIPAddress.Address, 0, ab, ref len);
    Console.WriteLine(BitConverter.ToString(ab, 0, 6));
}
catch (Exception ex) { }

或使用电脑名称

try
      {
           Tempaddr = System.Net.Dns.GetHostEntry("DESKTOP-xxxxxx");
      }
      catch (Exception ex) { }
      byte[] ab = new byte[6];
      int len = ab.Length, r = SendARP((int)Tempaddr.AddressList[1].Address, 0, ab, ref len);
        Console.WriteLine(BitConverter.ToString(ab, 0, 6));

这是我目前需求的最佳示例。但它有一个缺点,即它仅适用于IPv4。此外,在.Net Framework 4.7.2中,IPAddress.Address已被弃用。请使用以下代码进行替换:uint ipAddress = BitConverter.ToUInt32(hostIPAddress.GetAddressBytes(), 0);同样,这个方法假定了IPv4,无法与IPv6一起使用。 - stricq

2

只是一种更高效的已接受方法的改进版本。

    public string GetMacByIp( string ip )
    {
        var pairs = this.GetMacIpPairs();

        foreach( var pair in pairs )
        {
            if( pair.IpAddress == ip )
                return pair.MacAddress;
        }

        throw new Exception( $"Can't retrieve mac address from ip: {ip}" );
    }

    public IEnumerable<MacIpPair> GetMacIpPairs()
    {
        System.Diagnostics.Process pProcess = new System.Diagnostics.Process();
        pProcess.StartInfo.FileName = "arp";
        pProcess.StartInfo.Arguments = "-a ";
        pProcess.StartInfo.UseShellExecute = false;
        pProcess.StartInfo.RedirectStandardOutput = true;
        pProcess.StartInfo.CreateNoWindow = true;
        pProcess.Start();

        string cmdOutput = pProcess.StandardOutput.ReadToEnd();
        string pattern = @"(?<ip>([0-9]{1,3}\.?){4})\s*(?<mac>([a-f0-9]{2}-?){6})";

        foreach( Match m in Regex.Matches( cmdOutput, pattern, RegexOptions.IgnoreCase ) )
        {
            yield return new MacIpPair()
            {
                MacAddress = m.Groups[ "mac" ].Value,
                IpAddress = m.Groups[ "ip" ].Value
            };
        }
    }

    public struct MacIpPair
    {
        public string MacAddress;
        public string IpAddress;
    }

0

使用SharpPCap,它可以在Windows和Linux上运行

public string getMacAdress(string ip){
        LibPcapLiveDeviceList devices = LibPcapLiveDeviceList.Instance;//list all your network cards
        ARP arp = new ARP(devices[0]);//select the first network card by default
        IPAddress ip = IPAddress.Parse(ip);
        PhysicalAddress macAdress = arp.Resolve(ip);
        return macAdress.ToString();
}

0
我遇到了同样的问题,并创建了一个小型的MIT许可库,名为ArpLookupnuget),以解决这个任务。
它具有以下特点:
  • 不会创建任何系统进程
  • 不会解析某些CLI工具的输出
  • 适用于Windows和Linux
  • 在Linux上提供了一个异步的变体(在Windows上也有,但在那里无法真正实现异步)
  • 免费且开源
在使用dotnet add package ArpLookup之后,使用起来非常简单。
using System.Net.NetworkInformation;
using ArpLookup;

// ...

PhysicalAddress mac = Arp.Lookup(IPAddress.Parse("1.2.3.4"));

PhysicalAddress mac = await Arp.LookupAsync(IPAddress.Parse("1.2.3.4"));

-2
根据Marco Mp的评论,已使用ARP表。 arp class

  1. 您的问题要求“获取IP并查找MAC地址”,并且需要使用ARP而不是RARP(获取MAC并返回您当前使用的IP /主机)。您是如何到达这里的?
  2. 您所引用的网站使用了错误的进程定义(MAC到IP是RARP而不是ARP)...
- Khan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接