C#Async Ping:如何避免内存不足异常?

时间:2010-08-23 06:58:12

标签: c# .net asynchronous ping

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

问题是如果我只扫描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);
        }
    }

6 个答案:

答案 0 :(得分: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);
            }
        }
    }        
}

答案 1 :(得分:2)

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

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

答案 2 :(得分:1)

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

我会再次调查the Task Parallel Library,与the Task<T>相结合的Parallel.For结构应该会让您轻松。

注意:对于.Net 3.5用户,there is hope

答案 3 :(得分:1)

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

(myPing as IDisposable).Dispose()

因此,获取一个说明254个ping实例的列表异步运行(XXX1 / 254)并跟踪所有它们何时报告。当它们有,迭代你的ping实例列表,在每个上运行上面的代码实例,然后转储列表。

像魅力一样。

答案 4 :(得分:0)

伪代码

do

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

loop while morepings

答案 5 :(得分:0)

最后......根本不需要别针......

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