使用Lava.Lang.Process中的Xamarin对IP或HOSTNAME进行Ping操作

时间:2017-03-13 17:39:06

标签: c# android visual-studio mono xamarin.android

我尝试使用此代码,即使ip或主机名为TTL Expiredunreachable或任何其他不同的问题,它也始终返回timeout

using System.Net.NetworkInformation;

PingReply Reply = await pingo.SendPingAsync(text_ip.Text, 4000, buffer, options);
        Toast.MakeText(Activity, Reply.Status.ToString(), ToastLength.Long).Show();
        switch (Reply.Status)
        {
            case IPStatus.Success:

                Toast.MakeText(Activity, Reply.Status.ToString(), ToastLength.Long).Show();
                break;

            case IPStatus.TimedOut:

                Toast.MakeText(Activity, Reply.Status.ToString(), ToastLength.Long).Show();
                break;

            case IPStatus.TimeExceeded:

                Toast.MakeText(Activity, Reply.Status.ToString(), ToastLength.Long).Show();
                break;

            case IPStatus.DestinationHostUnreachable:


               Toast.MakeText(Activity, Reply.Status.ToString(), ToastLength.Long).Show();
                break;

            case IPStatus.DestinationNetworkUnreachable:

                Toast.MakeText(Activity, Reply.Status.ToString(), ToastLength.Long).Show();
                break;

            case IPStatus.DestinationUnreachable:

                Toast.MakeText(Activity, Reply.Status.ToString(), ToastLength.Long).Show();
                break;
        }

我发现了一个错误 https://github.com/mono/mono/blob/master/mcs/class/System/System.Net.NetworkInformation/Ping.cs(第339行)

并报告: https://bugzilla.xamarin.com/show_bug.cgi?id=53284

调用系统ping命令并检查退出代码:

if (!ping.WaitForExit (timeout) || (ping.HasExited && ping.ExitCode == 2))
    status = IPStatus.TimedOut;
else if (ping.ExitCode == 0)
    status = IPStatus.Success;
else if (ping.ExitCode == 1)
    status = IPStatus.TtlExpired;
  

如果没有超时,没有错误或成功(退出代码2或0),退出代码为1,则设置TTL过期。

     

如果ping根本没有收到任何回复数据包,它将以code 1退出。如果指定了数据包计数和截止时间,并且在截止时间到达之前收到的数据包数量少于计数数据包,则它也将以code 1退出。在其他错误上,它以code 2退出。否则它会以code 0退出。这使得可以使用退出代码来查看主机是否存活。

     

因此,返回值1表示没有收到响应(由于各种原因),Mono实现在这种情况下设置TTL expired状态。我希望在这种情况下会返回暂停状态。但似乎没有在此触发timeout Mono实现的TTL expired检测(例如,因为ping命令在它之前退出了自己的超时)。

     

所以还有其他可能的解决方案:

     

要将TTL expired视为主机不活动(但这会忽略真正的try { Process ipProcess = runtime.exec("ping -c 1 8.8.8.8"); int exitValue = ipProcess.waitFor(); ipProcess.destroy(); if(exitValue == 0) { // Success } else { // Failure } } catch (IOException | InterruptedException e) { e.printStackTrace(); } 检测)

无论如何,有人可以通过另一种方式表明这一点,例如:

Time in ms

从此Ttl valueStatisticsPacket LossReceived PacketsTransmited Packetspublic async void add_data(string adresa) { await Task.Delay(ping_interval); if (sekuenca < sekuenca_db && nderprit == false) { try { PingReply Reply = await pingo.SendPingAsync(text_ip.Text, timeout, buffer, options); switch (Reply.Status) { case IPStatus.Success: string sekuena = sekuenca.ToString(); mdata.Add(new data() { titulli = "From : " + Reply.Address.ToString(), sekuenca = "Secuenca : " + sekuena + ",", ttl = "TTL : " + Reply.Options.Ttl.ToString() + ",", madhesia = "Send : " + Reply.Buffer.Length.ToString() + " bytes", koha = "Time : " + Reply.RoundtripTime.ToString() + " ms" }); mAdapter.NotifyItemInserted(mdata.Count() - 1); if (ndaluar == false) { mRecyclerView.ScrollToPosition(mdata.Count() - 1); } time = time + Reply.RoundtripTime; koha.Add(Convert.ToInt32(Reply.RoundtripTime)); add_data(text_ip.Text); break; 和来自流程外壳的其他信息中获取对于每个ping secuence?

更新

如何为此目的实施它:

    X  Y       
    a  1
       1
       1
    b  1
       1
       1
    c  1 

1 个答案:

答案 0 :(得分:1)

使用操作系统获取ping时间的方法&#39; buit-in ping命令正在启动一个进程,读取其输出并解析该数据

所以采取必要的步骤:

  • 创建System.Diagnostics.Process
  • 正确设置其StartInfo-Parameters
  • 重定向其StandardOutput
  • 读取您可以访问的StreamReader的输出到字符串
  • 解析那个字符串。为此,您需要了解如何布置结果输出

对于第一部分(直到字符串),您的代码可能如下所示:

Process P = new Process();
P.StartInfo.FileName = "ping";
P.StartInfo.Arguments = "-c 3 8.8.8.8"; // Take 3 samples to 8.8.8.8
P.StartInfo.UseShellExecute = false;
P.StartInfo.RedirectStandardOutput = true;

string readData = "";
if (P.Start())
    readData = P.StandardOutput.ReadToEnd(); // This will also wait for the process to at least close its stdout
Console.Write(readData.ToString()); // Doing this, you will see how the output looks like

在此之后,您将在readData

中获得如下输出
64 bytes from 8.8.8.8: icmp_req=1 ttl=46 time=13.9 ms
64 bytes from 8.8.8.8: icmp_req=2 ttl=46 time=13.9 ms
64 bytes from 8.8.8.8: icmp_req=3 ttl=46 time=13.9 ms

--- 8.8.8.8 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 3016ms
rtt min/avg/max/mdev = 13.910/13.926/13.951/0.010 ms

您现在需要做的就是解析您感兴趣的相关部分。我建议使用System.Text.RegularExpressions.Regex来实现这一目标。我假设您对平均往返时间感兴趣。在这种情况下,您需要执行以下步骤:

  • 忽略除包含统计信息的最后一行以外的所有内容
  • 提取您感兴趣的值
  • 如果您需要的值为float(或double),请解析该值

        List<string> Lines = new List<string>(readData.Replace("\r\n", "\n").Split('\n'));
    
        while (Lines.Count > 0 && !Lines[0].StartsWith("---"))
            Lines.RemoveAt(0);
    
        // Here, we either have an empty list or a list starting with the line --- 8.8.8.8 ping statistics ---
        float avgPingTime = 0;
        if (Lines.Count > 2)
        {
            // Our important line is now Lines[2], the one starting with "rtt"
            Match M = Regex.Match(Lines[2], @"^rtt [^0-9]*([\d][^\/]+)\/([^\/]+)\/([^\/]+)\/([^ ]+) ms$");
            if (M != null && M.Success) // A match has indeed been found
            {
                string avgPingString = M.Groups[2].Value;
    
                // Now parse that value
                float.TryParse(avgPingString, System.Globalization.NumberStyles.Float, System.Globalization.NumberFormatInfo.InvariantInfo, out avgPingTime);
            }
        }
    
        if (avgPingTime > 0)
        {
            // We parsed the value correctly here
        }
    

但是,如果您对统计信息之前的每行的统计信息感兴趣,则需要采用不同的方法:

  

64字节来自8.8.8.8:icmp_req = 2 ttl = 46 time = 13.9 ms

这些行中有n个,所有这些行之前---开头的行:

List<string> Lines = new List<string>(readData.Replace("\r\n", "\n").Split('\n'));

while (Lines.Count > 0 && !Lines[0].StartsWith("---"))    
{
    Match M = Regex.Match(Lines[0], @"^[\d]+ bytes from ([^:]+): [^ ]+ ttl=([\d]+) time=([^ ]+) ms");

    if (M != null && M.Success)
    {
        string IP = M.Groups[1].Value;
        string TTL = M.Groups[2].Value;
        string timeStr = M.Groups[3].Value;

        Console.WriteLine(String.Format("Ping to {0} took {2} ms with a ttl of {1}", IP, TTL, timeStr));
        // Parsing the timeStr will work the same way as above
    }

    Lines.RemoveAt(0);
}