C#循环中多次ping [英] C# multiple pinging in loop

查看:321
本文介绍了C#循环中多次ping的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要创建将在循环中ping多个地址的应用程序.我在stackoverflow上阅读了很多示例,最后得到了可运行的代码:

I need to create application which will be pinging multiple addresses in loop. I read a lot of examples here at stackoverflow and finally got working code:

    public void Check(List<string> addresses)
    {
        List<Task<PingReply>> pingTasks = new List<Task<PingReply>>();
        foreach (string address in addresses)
        {
            pingTasks.Add(PingAsync(address));
        }

        Task.Factory.ContinueWhenAll(pingTasks.ToArray(), _ => { }).ContinueWith(t =>
        {
            StringBuilder pingResult = new StringBuilder();
            foreach (var pingTask in pingTasks)
            {
                pingResult.Append(pingTask.Result.Address);
                pingResult.Append("    ");
                pingResult.Append(pingTask.Result.Status);
                pingResult.Append("    ");
                pingResult.Append(pingTask.Result.RoundtripTime.ToString());
                pingResult.Append("   \n");
            }
            Console.WriteLine(pingResult.ToString());
        },
        CancellationToken.None,
        TaskContinuationOptions.None,
        TaskScheduler.FromCurrentSynchronizationContext());
    }

    public static Task<PingReply> PingAsync(string address)
    {
        var tcs = new TaskCompletionSource<PingReply>();
        using (Ping ping = new Ping())
        {
            ping.PingCompleted += (obj, sender) =>
            {
                tcs.SetResult(sender.Reply);
            };
            ping.SendAsync(address, new object());
        }
        return tcs.Task;
    }

现在,我需要更改此代码以使其与await和async一起使用,然后以一定间隔在循环中执行此代码.在这里,我的问题开始了.我不知道在这种情况下如何使用异步,我读了很多文章,但现在我很困惑,因为我的代码仍然无法正常工作.您能一步一步解释我如何更改我的代码以使其在等待中工作吗?您能解释一下如何在执行间隔时将其放入while循环吗?我试图将整个检查"功能放到循环中,并在最后添加Thread.Sleep(interval),但是我有一种奇怪的感觉,我做错了/效率低下.我需要在一秒钟内对400台服务器执行ping操作.可能吗? 问候

Now I need to change this code to works with await and async, and then execute this in loop with intervals. And here my problem starts. I have no idea how to use async in this case, I read many articles and now I'm confused because my code still doesn't work. Can you explain me step by step how to change my code to work with await? Can you explain me how can I put it inside while loop with executing intervals? I tried to put entire 'Check' function into the loop and add Thread.Sleep(interval) at the end but I have this strange feeling that I'm doing something wrong/unefficient. I need to ping 400 servers in 1 second. Is it even possible? Regards

更新1: 到目前为止,我有代码:

UPDATE 1: I have code so far:

using System;
using System.Collections.Generic;
using System.IO;
using System.Web;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Net.NetworkInformation;
using System.Linq;

namespace Pinging
{
    class CheckPing
    {
        public async Task LoopAndCheckPingAsync(List<string> addresses)
        {
            while (true)
            {
                var ping = new Ping();
                var pingTasks = addresses.Select(address => ping.SendPingAsync(address));

                await Task.WhenAll(pingTasks);

                StringBuilder pingResultBuilder = new StringBuilder();

                foreach (var pingReply in pingTasks)
                {
                    pingResultBuilder.Append(pingReply.Result.Address);
                    pingResultBuilder.Append("    ");
                    pingResultBuilder.Append(pingReply.Result.Status);
                    pingResultBuilder.Append("    ");
                    pingResultBuilder.Append(pingReply.Result.RoundtripTime.ToString());
                    pingResultBuilder.AppendLine();
                }

                Console.WriteLine(pingResultBuilder.ToString());

                await Task.Delay(TimeSpan.FromMinutes(5));
            }
        }
    }
}

并致电:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Pinging
{
    public class Class1
    {
        static void Main()
        {
            List<string> addresses = new List<string>() { "www.google.pl", "212.77.100.101" };

            CheckPing c = new CheckPing();
            Task.Factory.StartNew(() => c.LoopAndCheckPingAsync(addresses));

            Console.Read();
        }
    }
}

我尝试用不同的方式从Main调用LoopAndCheckPingAsync,但仍然冻结.这是我的最后尝试.

I tried call LoopAndCheckPingAsync from Main with different ways, but still freezes. This is my last try.

我进行了一些更改以查看应用程序性能,现在我的代码如下:

EDIT 2: I made some changes to see application performance and now my code looks like:

using System;
using System.Collections.Generic;
using System.IO;
using System.Web;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Net.NetworkInformation;
using System.Linq;

namespace Pinging
{
    class CheckPing
    {
        public async Task LoopAndCheckPingAsync(List<string> addresses)
        {
            while (true)
            {
                var pingTasks = addresses.Select(address =>
                {
                    return new Ping().SendPingAsync(address);
                });

                await Task.WhenAll(pingTasks);

                StringBuilder pingResultBuilder = new StringBuilder();

                foreach (var pingReply in pingTasks)
                {
                    pingResultBuilder.Append(pingReply.Result.Address);
                    pingResultBuilder.Append("    ");

                    pingResultBuilder.Append(pingReply.Result.Status);
                    pingResultBuilder.Append("    ");

                    pingResultBuilder.Append(pingReply.Result.RoundtripTime.ToString());
                    pingResultBuilder.AppendLine();
                }

                Console.WriteLine(pingResultBuilder.ToString());
                Functions.counter++;

                if (Functions.counter >= 100) break;

                await Task.Delay(TimeSpan.FromSeconds(1));
            }
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Pinging
{
    public class Class1
    {
        static void Main()
        {
            List<string> addresses = Functions.Read(@"C:\Users\Adam\Desktop\addresses.csv");

            Functions.start = DateTime.Now;

            CheckPing c = new CheckPing();
            c.LoopAndCheckPingAsync(addresses).Wait();

            Console.WriteLine(Functions.counter);

            Console.Read();
        }
    }
}

我正在使用从文件读取的标准站点地址:

I am using standard site addresses readed from file:

www.google.com
www.yahoo.com
www.live.com
www.msn.com
www.facebook.com
www.youtube.com
www.microsoft.com
www.wikipedia.org
www.myspace.com
www.ebay.com
www.aol.com
www.ask.com
www.craigslist.org
www.blogspot.com
www.answers.com
www.about.com
www.amazon.com
www.mapquest.com
www.windows.com
www.adobe.com
www.photobucket.com
www.wordpress.com
www.go.com
www.paypal.com
www.walmart.com
www.reference.com
www.cnn.com
www.twitter.com
www.imdb.com
www.flickr.com
www.att.com
www.cnet.com
www.irs.gov
www.whitepages.com
www.yellowpages.com
www.comcast.net
www.target.com
www.simplyhired.com
www.webmd.com
www.weather.com
www.blogger.com
www.bankofamerica.com
www.apple.com
www.chase.com
www.bizrate.com
www.hulu.com
www.merriam-webster.com
www.geocities.com
www.ehow.com
www.ezinearticles.com

现在一切正常,但是这是我需要处理的另一个问题.当我在5分钟后测试100000 ping时,出现内存不足异常.有办法解决这个问题吗?也许会划分成块并破坏旧类?

EDIT 3: Now everything works perfectly but here is another issue I need to handle. When I test 100000 pings after 5 minutes I get out of memory exception. Is there way to handle this somehow? Maybe dividing to blocks and destroys old classes?

错误内容:

未处理System.OutOfMemoryException HResult = -2147024882 消息=类型'System.OutOfMemoryException'的异常被抛出. 来源= mscorlib StackTrace: 在System.Exception.Init() 在System.InvalidOperationException..ctor处(字符串消息,Exception innerException) 在System.Net.NetworkInformation.PingException..ctor(字符串消息,Exception innerException) 在System.Net.NetworkInformation.Ping.ContinueAsyncSend(对象状态) 在System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object 状态) 在System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext,ContextCallback回调,对象状态,布尔值 reserveSyncCtx) 在System.Threading.ExecutionContext.Run(ExecutionContext executeContext,ContextCallback回调,对象状态,布尔值 reserveSyncCtx) 在System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() 在System.Threading.ThreadPoolWorkQueue.Dispatch() 在System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
InnerException:

System.OutOfMemoryException was unhandled HResult=-2147024882 Message=Exception of type 'System.OutOfMemoryException' was thrown. Source=mscorlib StackTrace: at System.Exception.Init() at System.InvalidOperationException..ctor(String message, Exception innerException) at System.Net.NetworkInformation.PingException..ctor(String message, Exception innerException) at System.Net.NetworkInformation.Ping.ContinueAsyncSend(Object state) at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() at System.Threading.ThreadPoolWorkQueue.Dispatch() at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
InnerException:

编辑5 :使用using语句添加后,我收到没有足够的存储空间来处理此命令"错误:

EDIT 5 After adding using statement I get "not enough storage is available to process this command" error:

未处理System.AggregateException HResult = -2146233088
Message =发生一个或多个错误.来源= mscorlib StackTrace: 在System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions) 在System.Threading.Tasks.Task.Wait(Int32毫秒超时,CancellationToken cancelToken) 在System.Threading.Tasks.Task.Wait() 在Pinging.Class1.Main() 在System.AppDomain._nExecuteAssembly(RuntimeAssembly程序集,String []参数) 在System.AppDomain.ExecuteAssembly(String assemblyFile,Evidence assemblySecurity,String [] args)中 在Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() 在System.Threading.ThreadHelper.ThreadStart_Context(对象状态) 在System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext,ContextCallback回调,对象状态,布尔值 reserveSyncCtx) 在System.Threading.ExecutionContext.Run(ExecutionContext executeContext,ContextCallback回调,对象状态,布尔值 reserveSyncCtx) 在System.Threading.ExecutionContext.Run(ExecutionContext执行上下文,ContextCallback回调,对象状态) 在System.Threading.ThreadHelper.ThreadStart()处InnerException:System.Net.NetworkInformation.PingException HResult = -2146233079 Message =在Ping请求期间发生异常. 来源= mscorlib 堆栈跟踪: 在System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task 任务) 在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task 任务) 在System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()处 在Pinging.CheckPing.d__2.MoveNext() InnerException:System.ApplicationException HResult = -2147024888 消息=没有足够的存储空间来处理此命令(HRESULT的异常:0x80070008) 来源= mscorlib 堆栈跟踪: 在System.Threading.ThreadPool.RegisterWaitForSingleObjectNative(WaitHandle waitHandle,对象状态,UInt32 timeOutInterval,布尔 executeOnlyOnce,RegisteredWaitHandle,registeredWaitHandle, StackCrawlMark& stackMark,布尔compressStack) 在System.Threading.ThreadPool.RegisterWaitForSingleObject(WaitHandle waitObject,WaitOrTimerCallback回调,对象状态,UInt32 millisecondsTimeOutInterval,布尔值executeOnlyOnce,StackCrawlMark& stackMark,布尔compressStack) 在System.Threading.ThreadPool.RegisterWaitForSingleObject(WaitHandle waitObject,WaitOrTimerCallback回调,对象状态,Int32 millisecondsTimeOutInterval,布尔型executeOnlyOnce) 在System.Net.NetworkInformation.Ping.InternalSend(IPAddress地址, Byte []缓冲区,Int32超时,PingOptions选项,布尔异步) 在System.Net.NetworkInformation.Ping.ContinueAsyncSend(对象状态) InnerException:

System.AggregateException was unhandled HResult=-2146233088
Message=One or more errors occurred. Source=mscorlib StackTrace: at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions) at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken) at System.Threading.Tasks.Task.Wait() at Pinging.Class1.Main() at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() at System.Threading.ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart() InnerException: System.Net.NetworkInformation.PingException HResult=-2146233079 Message=An exception occurred during a Ping request. Source=mscorlib StackTrace: at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() at Pinging.CheckPing.d__2.MoveNext() InnerException: System.ApplicationException HResult=-2147024888 Message=Not enough storage is available to process this command (Exception from HRESULT: 0x80070008) Source=mscorlib StackTrace: at System.Threading.ThreadPool.RegisterWaitForSingleObjectNative(WaitHandle waitHandle, Object state, UInt32 timeOutInterval, Boolean executeOnlyOnce, RegisteredWaitHandle registeredWaitHandle, StackCrawlMark& stackMark, Boolean compressStack) at System.Threading.ThreadPool.RegisterWaitForSingleObject(WaitHandle waitObject, WaitOrTimerCallback callBack, Object state, UInt32 millisecondsTimeOutInterval, Boolean executeOnlyOnce, StackCrawlMark& stackMark, Boolean compressStack) at System.Threading.ThreadPool.RegisterWaitForSingleObject(WaitHandle waitObject, WaitOrTimerCallback callBack, Object state, Int32 millisecondsTimeOutInterval, Boolean executeOnlyOnce) at System.Net.NetworkInformation.Ping.InternalSend(IPAddress address, Byte[] buffer, Int32 timeout, PingOptions options, Boolean async) at System.Net.NetworkInformation.Ping.ContinueAsyncSend(Object state) InnerException:

推荐答案

让我们逐步完成我们想做的事情:

Lets walk through what we want to do:

  1. 我们希望在首次执行该方法时开始while循环

  1. We want to begin the while loop when we first execute the method

我们想生成一堆任务,这些任务将用于发送ping请求.为此,我们可以使用 Ping.SendPingAsync .我们将使用

We want to generate a bunch of tasks which will be used to send the ping request. For that we can use Ping.SendPingAsync. We will project each element from the list of addresses using Enumerable.Select

我们将等到所有任务完成执行.为此,我们将在 Task.WhenAll .

We will wait untill all tasks finish executing. For that we will await on Task.WhenAll.

当所有任务完成执行ping请求后,我们将使用foreach循环对其进行迭代.

When all tasks finish executing the ping request, we will iterate them using a foreach loop.

我们将等待两次呼叫之间的间隔时间.我们不会使用Thread.Sleep,因为它是一个阻止调用.我们将改为使用 Task.Delay 将在内部使用Timer.当我们等待它时,控制权将返回给调用我们的方法.

We will wait on the interval time between the calls. We wont use Thread.Sleep, as it is a blocking call. We will instead use Task.Delay will internaly uses a Timer. When we await on it, control will yield back to the method that called us.

结果如下:

private static async Task LoopAndCheckPingAsync(List<string> addresses)
{  
    StringBuilder pingResultBuilder = new StringBuilder();        

    while (true)
    {
         var pingTasks = addresses.Select(address =>
         {
             using (var ping = new Ping())
             {
                 return ping.SendPingAsync(address);
             }
         }).ToList();    

        await Task.WhenAll(pingTasks);

        foreach (var pingReply in pingTasks)
        {                pingResultBuilder.Append(pingReply.Result.Address);
            pingResultBuilder.Append("    ");
            pingResultBuilder.Append(pingReply.Result.Status);
            pingResultBuilder.Append("    ");

            pingResultBuilder.Append(pingReply.Result.RoundtripTime.ToString());
            pingResultBuilder.AppendLine();
        }

        Console.WriteLine(pingResultBuilder.ToString());
        pingResultBuilder.Clear();

        await Task.Delay(TimeSpan.FromMinutes(5));
    }
}

请注意,该方法现在返回的是Task而不是void,因为我们需要在方法上使用await(请注意,一旦开始使用async,它就会在您的代码库中传播).

Note the method now returns a Task instead of void, because we need to await on our method (note async tends to spread in your codebase once you start using it).

修改

深入研究 Ping,显然我们不能在同一个Ping实例上执行多个ping请求(查看Ping.CheckStart会检查是否存在正在进行的请求,是否抛出InvalidOperationException),这正是我们使用Select方法.要解决此问题,我们可以为每个请求创建Ping类的实例.请注意,这将给您的应用程序增加一些内存压力.如果您有1000个请求同时进行,则意味着在发出这些请求时,内存中将有1000个Ping类的实例.

After looking a bit deeper into the Ping class, apparently we can't execute multiple ping requests on the same Ping instance (Look into Ping.CheckStart which checks if there's an ongoing request, and if there is throws an InvalidOperationException), which is exactly what we do in our Select method. To work around that problem, we can create an instance of the Ping class for each request. Note this will add some memory pressure to your application. If you have 1000 requests going on concurrently, that means you will have 1000 instances of the Ping class in memory while making those requests.

要注意的另一件事是,您正在内部使用ThreadPoolSynchronizationContext的控制台应用程序中运行.无需调用Task.Run即可执行我们的方法,可以在发出请求时使用Task.Wait使控制台应用程序保持活动状态.最好使用Wait,这样我们就可以查看是否从我们的方法传播了任何异常.

Another thing to note is you're running inside a Console Application which uses the ThreadPoolSynchronizationContext internally. There is no need to make a call to Task.Run to execute our method, you can use Task.Wait to keep the console application alive while making the requests. It is better to use Wait, so we can see if any exception propagated from our method.

static void Main()
{
    List<string> addresses = new List<string>() { "www.google.pl", "212.77.100.101" };

    CheckPing c = new CheckPing();
    c.LoopAndCheckPingAsync(addresses).Wait();
}

这篇关于C#循环中多次ping的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆