C#中的TraceRoute和Ping

33

有没有 C# 代码可供使用,用于对目标计算机进行 ping 和 traceroute?我正在寻找一种纯代码解决方案,而不是像我现在所做的那样调用 ping.exe 和 tracert.exe 程序并解析输出。我希望有更强大的解决方案。

7个回答

59

今天我必须编写一个TraceRoute类,因此我想分享源代码。

using System.Collections.Generic;
using System.Net.NetworkInformation;
using System.Text;
using System.Net;

namespace Answer
{  
  public class TraceRoute
  {
    private const string Data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";

    public static IEnumerable<IPAddress> GetTraceRoute(string hostNameOrAddress)
    {
      return GetTraceRoute(hostNameOrAddress, 1);
    }
    private static IEnumerable<IPAddress> GetTraceRoute(string hostNameOrAddress, int ttl)
    {
      Ping pinger = new Ping();
      PingOptions pingerOptions = new PingOptions(ttl, true);
      int timeout = 10000;
      byte[] buffer = Encoding.ASCII.GetBytes(Data);
      PingReply reply = default(PingReply);

      reply = pinger.Send(hostNameOrAddress, timeout, buffer, pingerOptions);

      List<IPAddress> result = new List<IPAddress>();
      if (reply.Status == IPStatus.Success)
      {
        result.Add(reply.Address);
      }
      else if (reply.Status == IPStatus.TtlExpired || reply.Status == IPStatus.TimedOut)
      {
        //add the currently returned address if an address was found with this TTL
        if (reply.Status == IPStatus.TtlExpired) result.Add(reply.Address);
        //recurse to get the next address...
        IEnumerable<IPAddress> tempResult = default(IEnumerable<IPAddress>);
        tempResult = GetTraceRoute(hostNameOrAddress, ttl + 1);
        result.AddRange(tempResult);
      }
      else
      {
        //failure 
      }

      return result;
    }
  }
}

以下是供需要的人使用的VB版本

Public Class TraceRoute
    Private Const Data As String = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"

    Public Shared Function GetTraceRoute(ByVal hostNameOrAddress As String) As IEnumerable(Of IPAddress)
        Return GetTraceRoute(hostNameOrAddress, 1)
    End Function
    Private Shared Function GetTraceRoute(ByVal hostNameOrAddress As String, ByVal ttl As Integer) As IEnumerable(Of IPAddress)
        Dim pinger As Ping = New Ping
        Dim pingerOptions As PingOptions = New PingOptions(ttl, True)
        Dim timeout As Integer = 10000
        Dim buffer() As Byte = Encoding.ASCII.GetBytes(Data)
        Dim reply As PingReply

        reply = pinger.Send(hostNameOrAddress, timeout, buffer, pingerOptions)

        Dim result As List(Of IPAddress) = New List(Of IPAddress)
        If reply.Status = IPStatus.Success Then
            result.Add(reply.Address)
        ElseIf reply.Status = IPStatus.TtlExpired Then
            'add the currently returned address
            result.Add(reply.Address)
            'recurse to get the next address...
            Dim tempResult As IEnumerable(Of IPAddress)
            tempResult = GetTraceRoute(hostNameOrAddress, ttl + 1)
            result.AddRange(tempResult)
        Else
            'failure 
        End If

        Return result
    End Function
End Class

2
很棒的解决方案。虽然只是小细节,但由于您使用了IEnumerable<>返回,您可以考虑使用yield return而不是填充列表。 - Jeff
2
如果有人偶然发现这篇文章,需要注意一些问题,比如可能永远无法返回并一直循环。请查看 https://dev59.com/snVC5IYBdhLWcg3w7V33#45565253 - caesay

21

这个实现方法简单、懒惰(适当可枚举),而且不会像其他答案那样一直搜索下去(maxTTL)。

public static IEnumerable<IPAddress> GetTraceRoute(string hostname)
{
    // following are similar to the defaults in the "traceroute" unix command.
    const int timeout = 10000;
    const int maxTTL = 30;
    const int bufferSize = 32;

    byte[] buffer = new byte[bufferSize];
    new Random().NextBytes(buffer);

    using (var pinger = new Ping())
    {
        for (int ttl = 1; ttl <= maxTTL; ttl++)
        {
            PingOptions options = new PingOptions(ttl, true);
            PingReply reply = pinger.Send(hostname, timeout, buffer, options);

            // we've found a route at this ttl
            if (reply.Status == IPStatus.Success || reply.Status == IPStatus.TtlExpired)
                yield return reply.Address;

            // if we reach a status other than expired or timed out, we're done searching or there has been an error
            if (reply.Status != IPStatus.TtlExpired && reply.Status != IPStatus.TimedOut)
                break;
        }
    }
}

如何处理 pinger 对象的释放? - apdevelop
你能展示一下这段代码的实现吗?比如说如何调用它。 - FlyingV
@FlyingV:你调用函数,然后返回路由。var routes = GetTraceRoute("google.com"); - caesay

15

8

关于Ping部分,请查看MSDN上的Ping类


以下代码示例演示了同步使用Ping类。请参阅Ping类。 - Kiquenet

2

这是我能想到的最有效的方法。 如果您喜欢它,请投票支持,让其他人也受益。

    using System;
    using System.Collections.Generic;
    using System.Net.NetworkInformation;

    namespace NetRouteAnalysis
    {
        class Program
        {
            static void Main(string[] args)
            {
                var route = TraceRoute.GetTraceRoute("8.8.8.8")

                foreach (var step in route)
                {
                    Console.WriteLine($"{step.Address,-20} {step.Status,-20} \t{step.RoundtripTime} ms");
                }
            }
        }

        public static class TraceRoute
        {
            public static IEnumerable<PingReply> GetTraceRoute(string hostnameOrIp)
            {
                // Initial variables
                var limit = 1000;
                var buffer = new byte[32];
                var pingOpts = new PingOptions(1, true);
                var ping = new Ping();               

                // Result holder.
                PingReply result = null;

                do
                {
                    result = ping.Send(hostnameOrIp, 4000, buffer, pingOpts);
                    pingOpts = new PingOptions(pingOpts.Ttl + 1, pingOpts.DontFragment);

                    if (result.Status != IPStatus.TimedOut)
                    {
                        yield return result;
                    }
                }
                while (result.Status != IPStatus.Success && pingOpts.Ttl < limit);
            }
        }       
    }

1

Ping: 我们可以使用内置在.NET Framework中的Ping类。

实例化一个Ping并订阅PingCompleted事件:

Ping pingSender = new Ping();
pingSender.PingCompleted += PingCompletedCallback;

添加代码以配置和执行ping操作,例如:

string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
byte[] buffer = Encoding.ASCII.GetBytes(data);
string who = "www.google.com";
AutoResetEvent waiter = new AutoResetEvent(false);
int timeout = 12000;

PingOptions options = new PingOptions(64, true);

pingSender.SendAsync(who, timeout, buffer, options, waiter);

添加一个PingCompletedEventHandler
public static void PingCompletedCallback(object sender, PingCompletedEventArgs e)
{
    ... Do stuff here
}

一个完整的工作例子的代码转储,基于MSDN的示例:

public static void Main(string[] args)
{
    string who = "www.google.com";
    AutoResetEvent waiter = new AutoResetEvent(false);

    Ping pingSender = new Ping();

    // When the PingCompleted event is raised,
    // the PingCompletedCallback method is called.
    pingSender.PingCompleted += 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);

    Console.WriteLine("Time to live: {0}", options.Ttl);
    Console.WriteLine("Don't fragment: {0}", options.DontFragment);

    // 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();
    Console.WriteLine("Ping example completed.");
}

public static void PingCompletedCallback(object sender, PingCompletedEventArgs e)
{
    // If the operation was canceled, display a message to the user.
    if (e.Cancelled)
    {
        Console.WriteLine("Ping canceled.");

        // Let the main thread resume. 
        // UserToken is the AutoResetEvent object that the main thread 
        // is waiting for.
        ((AutoResetEvent)e.UserState).Set();
    }

    // If an error occurred, display the exception to the user.
    if (e.Error != null)
    {
        Console.WriteLine("Ping failed:");
        Console.WriteLine(e.Error.ToString());

        // Let the main thread resume. 
        ((AutoResetEvent)e.UserState).Set();
    }

    Console.WriteLine($"Roundtrip Time: {e.Reply.RoundtripTime}");

    // Let the main thread resume.
    ((AutoResetEvent)e.UserState).Set();
}

0
作为对Scott上面代码答案的改进,我发现他的解决方案在路线在到达目的地之前逐渐消失时无法正常工作 - 它永远不会返回。一个更好的解决方案至少可以有部分路线,可能是这样的(我已经测试过它,它运行良好)。如果您想以其他方式控制迭代次数,可以将for循环中的“20”更改为更大或更小的值,或尝试检测是否花费了太长时间。原始代码归功于Scott - 谢谢。
    using System.Collections.Generic;
    using System.Net.NetworkInformation;
    using System.Text;
    using System.Net;

    ...

    public static void TraceRoute(string hostNameOrAddress)
    {
        for (int i = 1; i < 20; i++)
        {
            IPAddress ip = GetTraceRoute(hostNameOrAddress, i);
            if(ip == null)
            {
                break;
            }
            Console.WriteLine(ip.ToString());
        }
    }

    private static IPAddress GetTraceRoute(string hostNameOrAddress, int ttl)
    {
        const string Data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
        Ping pinger = new Ping();
        PingOptions pingerOptions = new PingOptions(ttl, true);
        int timeout = 10000;
        byte[] buffer = Encoding.ASCII.GetBytes(Data);
        PingReply reply = default(PingReply);

        reply = pinger.Send(hostNameOrAddress, timeout, buffer, pingerOptions);

        List<IPAddress> result = new List<IPAddress>();
        if (reply.Status == IPStatus.Success || reply.Status == IPStatus.TtlExpired)
        {
            return reply.Address;
        }
        else
        {
            return null;
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接