Ping.SendAsync陷入无限循环

问题描述

我正在尝试获取本地网络上所有主机的列表。在this Stackoverflow thread之后。但是Ping.SendAsync()陷入无限循环,即使我保持很少的超时时间,也就是20。这是我的代码

        static CountdownEvent countdown;
        static int upCount = 0;
        static object lockObj = new object();
        const bool resolveNames = false;
        static List<string> activeInterfaces = new List<string>();

        static void p_PingCompleted(object sender,PingCompletedEventArgs e)
        {
            try
            {

                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
                    //{
                    activeInterfaces.Add(ip);
                    Console.WriteLine("{0} is up: ({1} ms)",e.Reply.roundtripTime);
                    //}
                    lock (lockObj)
                    {
                        upCount++;
                    }
                }
                else if (e.Reply == null)
                {
                    Console.WriteLine("Pinging {0} Failed. (Null Reply object?)",ip);
                }
                countdown.Signal();
            }
            catch (Exception exp)
            {
                Console.WriteLine("Here you go...");
            }

        }


        [HttpGet]
        [Route("api/pc/getonlinePCs")]
        public List<string> GetonlinePCs()
        {

            activeInterfaces.Clear();
            //List<string> activeInterfaces=new List<string>();
            string ipBase = "";

            var host = Dns.GetHostEntry(Dns.GetHostName());
            foreach (var ip in host.AddressList)
            {
                if (ip.AddressFamily == AddressFamily.InterNetwork)
                {
                    ipBase = ip.ToString().Substring(0,(ip.ToString().LastIndexOf(".") + 1));//"10.22.4.";

                }
            }


            countdown = new CountdownEvent(1);
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for (int i = 2; i < 254; i++)
            {
                string ip = ipBase + i.ToString();

                //var tcpClient = new TcpClient();
                //tcpClient.Connected += new PingCompletedEventHandler(p_PingCompleted);
                Ping ping = new Ping();
                ping.PingCompleted += new PingCompletedEventHandler(p_PingCompleted);
                countdown.AddCount();
                ping.SendAsync(ip,20,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();
     

            return activeInterfaces;
        }

我不认为SO线程提供的解决方案已经过期,而是因为我是前端人员,所以我会犯一个小逻辑错误。谢谢!

修改

根据专家的评论,我将SendAsync代码包装在try / catch中,并最终阻止为

              try
                {
                    Ping ping = new Ping();
                    ping.PingCompleted += new PingCompletedEventHandler(p_PingCompleted);
                    countdown.AddCount();
                    ping.SendAsync(ip,ip);
                }
                catch(Exception exception)
                {
                    Console.WriteLine($"Exception on {ip} .Technical details: {exception}");
                }
                finally
                {
                    countdown.AddCount();
                }

但是,当我调试代码时,它总是落在最后的块上。没有异常块,并且在253次ping迭代完成后,再次出现无限循环! @Evk和@jdweng请帮助!

@Evk是其卡住方式的图像

enter image description here

解决方法

Ping SendAsync并没有达到您的期望,您可以释放资源,但是您需要自己处理从另一个线程返回的答案,您需要挂钩PingCompleted来处理答案。

最好通过调用ping和方法在方法中创建异步方法,并通过自己实现方法在async方法中调用方法 https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/how-to-wrap-eap-patterns-in-a-task

Ping pingSender = new Ping ();

// When the PingCompleted event is raised,// the PingCompletedCallback method is called.
pingSender.PingCompleted += new PingCompletedEventHandler (PingCompletedCallback);

// Create a buffer of 32 bytes of data to be transmitted.
string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
byte [] buffer = Encoding.ASCII.GetBytes (data);

// Wait 12 seconds for a reply.
int timeout = 12000;

// Set options for transmission:
// The data can go through 64 gateways or routers
// before it is destroyed,and the data packet
// cannot be fragmented.
PingOptions options = new PingOptions (64,true);

// Send the ping asynchronously.
// Use the waiter as the user token.
// When the callback completes,it can wake up this thread.
pingSender.SendAsync (who,timeout,buffer,options,waiter);

// Prevent this example application from ending.
// A real application should do something useful
// when possible.
waiter.WaitOne ();
,

我想它不是在回答您的问题,而是在回答您的任务,您在蛮横地使用IP请求查看IP地址,最好使用地址解析协议(也称为ARP)。

首先,创建一个存储数据的结构:

public struct MacIpPair : IEquatable<MacIpPair>
{
    /// <summary>
    /// Initializes a new instance of the <see cref="MacIpPair"/> struct.
    /// </summary>
    /// <param name="mac">The mac.</param>
    /// <param name="ip">The ip.</param>
    /// <param name="type">ARP record status</param>
    /// <exception cref="System.ArgumentException">Mac address needs to be provided - mac</exception>
    /// <exception cref="System.ArgumentException">IP address needs to be provided - ip</exception>
    public MacIpPair(string mac,string ip,ArpStatus type)
    {
        if (string.IsNullOrEmpty(mac))
        {
            throw new System.ArgumentException("Mac address needs to be provided",nameof(mac));
        }

        if (string.IsNullOrEmpty(ip))
        {
            throw new System.ArgumentException("IP address needs to be provided",nameof(ip));
        }

        MacAddress = mac;
        IpAddress = ip;
        Status = type;
    }

    /// <summary>
    /// The mac address
    /// </summary>
    /// <value>The mac address.</value>
    [NotNull]
    public string MacAddress { get; }

    /// <summary>
    /// The ip address
    /// </summary>
    /// <value>The ip address.</value>
    [NotNull]
    public string IpAddress { get; }
    /// <summary>
    /// The status of the ARP entry
    /// </summary>
    ArpStatus Status { get; }

    /// <summary>
    /// Determines whether the specified <see cref="System.Object" /> is equal to this instance.
    /// </summary>
    /// <param name="obj">The object to compare with the current instance.</param>
    /// <returns><c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise,<c>false</c>.</returns>
    public override bool Equals(object? obj)
    {
        return obj is MacIpPair pair && Equals(pair);
    }

    /// <summary>
    /// Indicates whether the current object is equal to another object of the same type.
    /// </summary>
    /// <param name="other">An object to compare with this object.</param>
    /// <returns><see langword="true" /> if the current object is equal to the <paramref name="other" /> parameter; otherwise,<see langword="false" />.</returns>
    public bool Equals(MacIpPair other)
    {
        return MacAddress == other.MacAddress &&
                IpAddress == other.IpAddress &&
                Status==other.Status
                ;
    }

    /// <summary>
    /// Returns a hash code for this instance.
    /// </summary>
    /// <returns>A hash code for this instance,suitable for use in hashing algorithms and data structures like a hash table.</returns>
    public override int GetHashCode()
    {
        return HashCode.Combine(MacAddress,IpAddress,Status);
    }

    /// <summary>
    /// Implements the == operator.
    /// </summary>
    /// <param name="left">The left.</param>
    /// <param name="right">The right.</param>
    /// <returns>The result of the operator.</returns>
    public static bool operator ==(MacIpPair left,MacIpPair right)
    {
        return left.Equals(right);
    }

    /// <summary>
    /// Implements the != operator.
    /// </summary>
    /// <param name="left">The left.</param>
    /// <param name="right">The right.</param>
    /// <returns>The result of the operator.</returns>
    public static bool operator !=(MacIpPair left,MacIpPair right)
    {
        return !(left == right);
    }
}

具有您可以填充的结构:

/// <summary>
/// Gets all mac addresses and IP PAIRS visible to the computer.
/// </summary>
/// <remarks>
/// This will use RRP broadcast to obtain the mac addresses for all devices connected with the DHCP server
/// </remarks>
/// <returns>List&lt;MacIpPair&gt;.</returns>
public static List<MacIpPair> GetAllMacAddressesAndIpPairs()
{
    try
    {
        List<MacIpPair> mip = new List<MacIpPair>();
        using 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();
        pProcess.WaitForExit();
        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))
        {
            if (m is null)
                continue;
            mip.Add(new MacIpPair(m.Groups["mac"].Value,m.Groups["ip"].Value,ArpStatus.Static));
        }

        return mip;
    }
    catch (Exception e)
    {
        Walter.TicketService.PostException(e);
        throw;
    }
}

此代码来自我的Nuget软件包Walter.Net.Networking,使用类Walter.Net.Networking.LocalNetwork

如果上述包装器失败,我还有一个包装器会调用本机Windows方法。

[DllImport("IpHlpApi.dll",EntryPoint = "GetIpNetTable")]
[return: MarshalAs(UnmanagedType.U4)]
internal static extern int GetIpNetTable(IntPtr pIpNetTable,[MarshalAs(UnmanagedType.U4)] ref int pdwSize,bool bOrder);