1

问题:我想搜索子网中的所有计算机。所以我向子网中的所有 IP 地址发送 ping。

问题是如果我只扫描 192.168.0 就可以正常工作。“。但是如果我扫描 192.168.. *”,那么我会得到一个“内存不足”异常。

为什么 ?我是否必须限制线程,或者问题是新 ping 消耗的内存在完成后不会被破坏,还是我需要调用 gc.collect() ?

    static void Main(string[] args)
    { 
        string strFromIP = "192.168.0.1";
        string strToIP = "192.168.255.255";

        Oyster.Math.IntX omiFromIP = 0;
        Oyster.Math.IntX omiToIP = 0;
        IsValidIP(strFromIP, ref omiFromIP);
        IsValidIP(strToIP, ref omiToIP);
        for (Oyster.Math.IntX omiThisIP = omiFromIP; omiThisIP <= omiToIP; ++omiThisIP)
        {
            Console.WriteLine(IPn2IPv4(omiThisIP));
            System.Net.IPAddress sniIPaddress = System.Net.IPAddress.Parse(IPn2IPv4(omiThisIP));
            SendPingAsync(sniIPaddress);
        }

        Console.WriteLine(" --- Press any key to continue --- ");
        Console.ReadKey();
    } // Main


    // http://pberblog.com/post/2009/07/21/Multithreaded-ping-sweeping-in-VBnet.aspx
    // http://www.cyberciti.biz/faq/how-can-ipv6-address-used-with-webbrowser/#comments
    // http://www.kloth.net/services/iplocate.php
    // http://bytes.com/topic/php/answers/829679-convert-ipv4-ipv6
    // http://stackoverflow.com/questions/1434342/ping-class-sendasync-help
    public static void SendPingAsync(System.Net.IPAddress sniIPaddress)
    {
        int iTimeout = 5000;
        System.Net.NetworkInformation.Ping myPing = new System.Net.NetworkInformation.Ping();
        System.Net.NetworkInformation.PingOptions parmPing = new System.Net.NetworkInformation.PingOptions();

        System.Threading.AutoResetEvent waiter = new System.Threading.AutoResetEvent(false);
        myPing.PingCompleted += new System.Net.NetworkInformation.PingCompletedEventHandler(AsyncPingCompleted);
        string data = "ABC";
        byte[] dataBuffer = Encoding.ASCII.GetBytes(data);

        parmPing.DontFragment = true;
        parmPing.Ttl = 32;

        myPing.SendAsync(sniIPaddress, iTimeout, dataBuffer, parmPing, waiter);
        //waiter.WaitOne();
    }


    private static void AsyncPingCompleted(Object sender, System.Net.NetworkInformation.PingCompletedEventArgs e)
    {

        System.Net.NetworkInformation.PingReply reply = e.Reply;
        ((System.Threading.AutoResetEvent)e.UserState).Set();
        if (reply.Status == System.Net.NetworkInformation.IPStatus.Success)
        {
            Console.WriteLine("Address: {0}", reply.Address.ToString());
            Console.WriteLine("Roundtrip time: {0}", reply.RoundtripTime);
        }
    }
4

6 回答 6

2

根据这个线程System.Net.NetworkInformation.Ping似乎为每个异步请求分配一个线程,并且“ping-sweeping B 类网络会创建 100 个线程并最终导致内存不足错误。”

该人使用的解决方法是使用原始套接字编写自己的实现。当然,您不必在 F# 中这样做,但这样做有很多优点。

于 2010-08-23T18:34:07.037 回答
2

第一:第一次只启动 1000 次 ping(在 Main 的循环中)

二:将以下参数移动到Program类(成员变量)

Oyster.Math.IntX omiFromIP = 0; 
Oyster.Math.IntX omiToIP = 0;
Oyster.Math.IntX omiCurrentIp = 0;
object syncLock = new object();

第三:在 AsyncPingCompleted 的底部做这样的事情:

public void AsyncPingCompleted (bla bla bla)
{
    //[..other code..]

    lock (syncLock) 
    {
        if (omiToIP < omiCurrentIp)
        {
           ++omiCurrentIp;
           System.Net.IPAddress sniIPaddress = System.Net.IPAddress.Parse(IPn2IPv4(omiCurrentIp)); 
           SendPingAsync(sniIPaddress); 
        }
    }
}

更新完整的代码示例

public class Example
{
    // Number of pings that can be pending at the same time
    private const int InitalRequests = 10000;

    // variables from your Main method
    private Oyster.Math.IntX _omiFromIP = 0;
    private Oyster.Math.IntX _omiToIP = 0;
    private Oyster.Math.IntX _omiCurrentIp = 0;

    // synchronoize so that two threads
    // cannot ping the same IP.
    private object _syncLock = new object();

    static void Main(string[] args)
    {
        string strFromIP = "192.168.0.1";
        string strToIP = "192.168.255.255";

        IsValidIP(strFromIP, ref _omiFromIP);
        IsValidIP(strToIP, ref _omiToIP);
        for (_omiCurrentIp = _omiFromIP; _omiCurrentIp <= _omiFromIP + InitalRequests; ++_omiCurrentIp)
        {
            Console.WriteLine(IPn2IPv4(_omiCurrentIp));
            System.Net.IPAddress sniIPaddress = System.Net.IPAddress.Parse(IPn2IPv4(_omiCurrentIp));
            SendPingAsync(sniIPaddress);
        }

        Console.WriteLine(" --- Press any key to continue --- ");
        Console.ReadKey();
    } // Main


    // http://pberblog.com/post/2009/07/21/Multithreaded-ping-sweeping-in-VBnet.aspx
    // http://www.cyberciti.biz/faq/how-can-ipv6-address-used-with-webbrowser/#comments
    // http://www.kloth.net/services/iplocate.php
    // http://bytes.com/topic/php/answers/829679-convert-ipv4-ipv6
    // http://stackoverflow.com/questions/1434342/ping-class-sendasync-help
    public void SendPingAsync(System.Net.IPAddress sniIPaddress)
    {
        int iTimeout = 5000;
        System.Net.NetworkInformation.Ping myPing = new System.Net.NetworkInformation.Ping();
        System.Net.NetworkInformation.PingOptions parmPing = new System.Net.NetworkInformation.PingOptions();

        System.Threading.AutoResetEvent waiter = new System.Threading.AutoResetEvent(false);
        myPing.PingCompleted += new System.Net.NetworkInformation.PingCompletedEventHandler(AsyncPingCompleted);
        string data = "ABC";
        byte[] dataBuffer = Encoding.ASCII.GetBytes(data);

        parmPing.DontFragment = true;
        parmPing.Ttl = 32;

        myPing.SendAsync(sniIPaddress, iTimeout, dataBuffer, parmPing, waiter);
        //waiter.WaitOne();
    }


    private void AsyncPingCompleted(Object sender, System.Net.NetworkInformation.PingCompletedEventArgs e)
    {

        System.Net.NetworkInformation.PingReply reply = e.Reply;
        ((System.Threading.AutoResetEvent)e.UserState).Set();
        if (reply.Status == System.Net.NetworkInformation.IPStatus.Success)
        {
            Console.WriteLine("Address: {0}", reply.Address.ToString());
            Console.WriteLine("Roundtrip time: {0}", reply.RoundtripTime);
        }


        // Keep starting those async pings until all ips have been invoked.
        lock (_syncLock)
        {
            if (_omiToIP < _omiCurrentIp)
            {
                ++_omiCurrentIp;
                System.Net.IPAddress sniIPaddress = System.Net.IPAddress.Parse(IPn2IPv4(_omiCurrentIp));
                SendPingAsync(sniIPaddress);
            }
        }
    }        
}
于 2010-08-23T12:08:48.443 回答
1

我想问题是你几乎同时产生了大约 63K 的 ping 请求。如果没有进一步的内存分析,很难说哪些部分消耗了内存。您正在使用可能有限的网络资源。限制活动 ping 的数量将简化本地资源的使用以及网络流量。

我将再次研究Task Parallel Library,与Parallel.For相结合的构造应该Task<T>会让您轻松。

注意:对于 .Net 3.5 用户,有希望

于 2010-08-23T07:37:52.283 回答
1

我做了类似的事情。我解决项目问题的方法是将 ping 实例转换为 IDisposable:

(myPing as IDisposable).Dispose()

因此,获取一个异步运行的 254 个 ping 实例的列表(XXX1/254),并跟踪所有这些实例的报告时间。当它们有报告时,遍历您的 ping 实例列表,在每个实例上运行上述代码,然后转储列表。

奇迹般有效。

于 2013-08-17T18:46:26.967 回答
0

最后......根本不需要ping......

http://www.codeproject.com/KB/cs/c__ip_scanner.aspx

我需要做的就是让它在调试时线程安全。更改添加到:

void Add( string m )
{
    Invoke(new MethodInvoker(
        delegate
        {
            add.Items.Add(m);
        }));
    //add.Items.Add( m );
}

 

Private Sub Add(m As String)
    Invoke(New MethodInvoker(Function() Do
        add.Items.Add(m)
    End Function))
    'add.Items.Add(m);'
End Sub
于 2010-08-24T22:44:26.367 回答
0

伪代码

do

if pings_running > 100 then
sleep 100ms.
else
start ping
endif

loop while morepings
于 2010-08-23T12:24:26.863 回答