如何获取局域网中所有主机的IP地址?

33

我需要列出局域网中所有已连接主机的IP地址。 最简单的方法是什么?


1
定义连接 - 你的意思是像活动的吗?它们被防火墙保护还是可以被ping到? - TomTom
是的,它们可以被防火墙保护,但不一定需要 - 我不知道因为我是用户而不是管理员。 - Saint
1
这是在什么环境下?你有支持SNMP的托管交换机吗?如果有,你可以查询交换机以查看谁是活动状态。如果只是一个小型家用路由器,那就没办法了。 - Tim Coker
@TimCoker 是正确的。Pint很棒,但只有在机器配置为回复ping时才能使用。SNMP可能是获取真实统计数据的最佳方式。 - Rusty Nail
8个回答

59

您需要进行ping扫描。在System.Net命名空间中有一个Ping类。以下是示例。如果计算机正在运行防火墙,则只有通过对交换机进行SNMP查询才能确定此信息。

System.Net.NetworkInformation.Ping p = new System.Net.NetworkInformation.Ping();
System.Net.NetworkInformation.PingReply rep = p.Send("192.168.1.1");
if (rep.Status == System.Net.NetworkInformation.IPStatus.Success)
{
    //host is active
}

另一个问题是确定你的网络有多大。在大多数家庭环境中,网络掩码将为24位。这意味着设置为255.255.255.0。如果你的网关是192.168.1.1,则意味着你的网络上有效地址将是192.168.1.1到192.168.1.254。这里有一个IP计算器可以帮助你。你需要循环遍历每个地址并使用Ping类ping该地址并检查PingReply。
如果你只是想获取信息而不关心如何获取,可以使用NMap。命令如下:
nmap -sP 192.168.1.0/24

编辑:

就速度而言,因为您在本地网络上,所以可以将超时间隔大大缩短,因为您的计算机不应该需要超过100毫秒来回复。您还可以使用SendAsync并行地ping所有计算机。以下程序将在不到半秒钟内ping 254个地址。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.NetworkInformation;
using System.Diagnostics;
using System.Net;
using System.Threading;
using System.Net.Sockets;

namespace ConsoleApplication1
{
    class Program
    {
        static CountdownEvent countdown;
        static int upCount = 0;
        static object lockObj = new object();
        const bool resolveNames = true;

        static void Main(string[] args)
        {
            countdown = new CountdownEvent(1);
            Stopwatch sw = new Stopwatch();
            sw.Start();
            string ipBase = "10.22.4.";
            for (int i = 1; i < 255; i++)
            {
                string ip = ipBase + i.ToString();

                Ping p = new Ping();
                p.PingCompleted += new PingCompletedEventHandler(p_PingCompleted);
                countdown.AddCount();
                p.SendAsync(ip, 100, ip);
            }
            countdown.Signal();
            countdown.Wait();
            sw.Stop();
            TimeSpan span = new TimeSpan(sw.ElapsedTicks);
            Console.WriteLine("Took {0} milliseconds. {1} hosts active.", sw.ElapsedMilliseconds, upCount);
            Console.ReadLine();
        }

        static void p_PingCompleted(object sender, PingCompletedEventArgs e)
        {
            string ip = (string)e.UserState;
            if (e.Reply != null && e.Reply.Status == IPStatus.Success)
            {
                if (resolveNames)
                {
                    string name;
                    try
                    {
                        IPHostEntry hostEntry = Dns.GetHostEntry(ip);
                        name = hostEntry.HostName;
                    }
                    catch (SocketException ex)
                    {
                        name = "?";
                    }
                    Console.WriteLine("{0} ({1}) is up: ({2} ms)", ip, name, e.Reply.RoundtripTime);
                }
                else
                {
                    Console.WriteLine("{0} is up: ({1} ms)", ip, e.Reply.RoundtripTime);
                }
                lock(lockObj)
                {
                    upCount++;
                }
            }
            else if (e.Reply == null)
            {
                Console.WriteLine("Pinging {0} failed. (Null Reply object?)", ip);
            }
            countdown.Signal();
        }
    }
}

编辑:在我自己使用了一段时间后,我修改了程序以输出响应的IP数量。有一个const布尔值,如果设置为true,将导致程序解析IP的主机名。尽管这会显著减慢扫描速度(从不到半秒钟到16秒),但也可以发现,如果IP地址输入错误(我自己输错了),则回复对象可能为空,因此我进行了处理。


1
@Brad,不确定是否有完整的API,但它是按照常规UNIX程序的精神设计的,因此您可以轻松地通过命令行执行它并捕获标准输出。还有选项以XML格式或GREPable格式(即与正则表达式一起使用)格式化。有关如何在C#中执行命令并捕获输出的信息,请参见此答案。http://stackoverflow.com/questions/3258704/issues-about-files-in-use-get-the-name-of-another-process-that-use-file/3258779#3258779 - Tim Coker
2
没有理由让多个独立的IP子网共享同一个物理局域网,因此这种过程是有限制的(并且不会区分由交换机连接的局域网)。而且这还没有考虑到VLAN。对于这个问题的真正答案是“一般情况下没有这样的方法”。在实践中,ping每个可达地址而不命中网关(即where this-IP & mask == other-IP & mask)将起作用,除非其他节点不响应ping。 - Richard
@TimCoker,是的,它可以工作,但如果我要ping例如255个主机,则需要几秒钟。我能加速这个过程吗? - Saint
@Saint_pl 这有意义吗?你需要解释些什么吗? - Tim Coker
1
这是惊人的代码,更像是艺术。 根据我的偏好进行了轻微的更改,现在它像风暴一样运行。 - Gaurav Gandhi
显示剩余5条评论

2
您需要构建一个地址范围(例如192.168.0.1 - 192.168.255.254),并对每个地址进行ping测试。如果收到响应,则表示该主机是活动的。
异步Ping教程:

http://www.geekpedia.com/tutorial234_Asynchronous-Ping-using-Csharp.html

然而,一些较新的操作系统会阻止 ping 请求(ICMP)。为了让您收到响应,需要在每台计算机的防火墙中禁用它。

2
Nazim是正确的。而krowe2的回复事实上是错误的。交换机绝对会转发广播流量。您可以在以下情况下向子网的广播地址发送ping。
1.a)您在同一个子网上。
1.b)网关允许IP直接广播
2.a)该子网不是织物网络的一部分。
2.b)如果该子网是织物的一部分,则允许在该子网上进行广播流量。
在大约80%的网络中(以及使用192.168.1.1作为网关的网络中约99.99%),这将起作用。我是一名网络工程师,我经常这样做。 您不能依靠ICMP(ping)响应来验证网络上设备的存在。问题在于没有任何设备需要听取任何流量。不是ping,不是SNMP,也不是NetBios。唯一真正的方法是查看ARP表。
您必须向子网中的每个IP发送任何类型的流量,并查看自己的ARP表。因为在解析本地子网上的IP的MAC地址之前,没有设备可以获取任何流量。因此,在设备决定是否要侦听流量之前,它必须响应ARP请求。然后,您的设备将此缓存到其ARP表中,并可以向其他设备发送具有正确目标MAC的帧。 在Windows PC上,命令行命令是“arp -a”。如果我记得正确的话,Linux是“arp -n”。

1
你可以使用托管代码完成所有操作。我使用System.DirectoryServices和System.Net进行操作。基本上,我从DirectoryServices获取本地计算机的名称(在处理域和工作组时),然后从System.Net.Dns获取每个主机的IP地址。这是一个方便的类,将所有内容打包在一起...
public class NetworkComputer {
    private string _domain;
    private string _name;
    private IPAddress[] _addresses = null;

    public string Domain { get { return _domain; } }
    public string Name { get { return _name; } }
    public IPAddress[] Addresses { get { return _addresses; } }

    private NetworkComputer(string domain, string name) {
        _domain = domain;
        _name = name;
        try { _addresses = Dns.GetHostAddresses(name); } catch { }
    }

    public static NetworkComputer[] GetLocalNetwork() {
        var list = new List<NetworkComputer>();
        using(var root = new DirectoryEntry("WinNT:")) {
            foreach(var _ in root.Children.OfType<DirectoryEntry>()) {
                switch(_.SchemaClassName) {
                    case "Computer":
                        list.Add(new NetworkComputer("", _.Name));
                        break;
                    case "Domain":
                        list.AddRange(_.Children.OfType<DirectoryEntry>().Where(__ => (__.SchemaClassName == "Computer")).Select(__ => new NetworkComputer(_.Name, __.Name)));
                        break;
                }
            }
        }
        return list.OrderBy(_ => _.Domain).ThenBy(_ => _.Name).ToArray();
    }
}

然后只需调用静态方法,即可获取您的局域网计算机数组...

var localComputers = NetworkComputer.GetLocalNetwork();

0
你可以对地址范围进行ping测试,并记录哪些主机有响应。当然,这需要主机响应ping数据包。

Ping 例如 255 台主机有点耗时,我认为是这样的。有更快的方法吗? - Saint
2
一秒钟并不耗时。只需同时击中所有255个ping,而不是一个接一个地等待超时。 - TomTom
如何更快地完成这个任务?即使我ping 10个主机,也需要几秒钟的时间。 - Saint

0
namespace WindowsFormsApplication1
{
    class WifiInformation
    {
        static CountdownEvent countdown;
        static int upCount = 0;
        static object lockObj = new object();
        const bool resolveNames = true;

        static void Main(string[] args)
        {
            string ipBase = getIPAddress();
            string[] ipParts = ipBase.Split('.');
            ipBase = ipParts[0] + "." + ipParts[1] + "." + ipParts[2] + ".";
            for (int i = 1; i < 255; i++)
            {
                string ip = ipBase + i.ToString();

                Ping p = new Ping();
                p.PingCompleted += new PingCompletedEventHandler(p_PingCompleted);
                p.SendAsync(ip, 100, ip);
            }
            Console.ReadLine();
        }

        static void p_PingCompleted(object sender, PingCompletedEventArgs e)
        {
            string ip = (string)e.UserState;
            if (e.Reply != null && e.Reply.Status == IPStatus.Success)
            {
                if (resolveNames)
                {
                    string name;
                    try
                    {
                        IPHostEntry hostEntry = Dns.GetHostEntry(ip);
                        name = hostEntry.HostName;
                    }
                    catch (SocketException ex)
                    {
                        name = "?";
                    }
                    Console.WriteLine("{0} ({1}) is up: ({2} ms)", ip, name, e.Reply.RoundtripTime);
                }
                else
                {
                    Console.WriteLine("{0} is up: ({1} ms)", ip, e.Reply.RoundtripTime);
                }
                lock (lockObj)
                {
                    upCount++;
                }
            }
            else if (e.Reply == null)
            {
                Console.WriteLine("Pinging {0} failed. (Null Reply object?)", ip);
            }
        }

        public static string getIPAddress()
        {
            IPHostEntry host;
            string localIP = "";
            host = Dns.GetHostEntry(Dns.GetHostName());
            foreach (IPAddress ip in host.AddressList)
            {
                if (ip.AddressFamily == AddressFamily.InterNetwork)
                {
                    localIP = ip.ToString();
                }
            }
            return localIP;
        }
    }
}

-1

向全局广播IP地址(192.168.1.255)发送Ping请求

通常情况下,我们将从连接到局域网的所有主机获得回复。或者,如果您有任何特定于局域网的网络,请ping该网络的广播ID(即该范围内的最后一个IP地址)。

然后,您就可以知道连接到局域网中的所有IP地址。


什么?真的吗? - L.Trabacchin
以前我们使用以太网集线器时,这个方法是可行的,但现在所有交换机都会忽略IP广播并且不会将这些消息转发到所有端口。 - krowe2

-1

1
DHCP服务器不太可能返回“活动状态”。您只知道最近的活动 - 仍然需要ping,而且您可能会错过一些静态配置的内容。 - TomTom
是的,确保的唯一方法就是搜索您的网络交换机,然后跟踪所有电缆。 - joni

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