问题描述
我正在尝试获取本地网络上所有主机的列表。在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请帮助!
解决方法
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<MacIpPair>.</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);