适用于 .NET Framework 2.0 的 Tracert 组件






4.87/5 (23投票s)
本文基于 .NET Framework 2.0 中提供的 Ping 类实现了一个 tracert 组件。
引言
Tracert 是 Windows 中的一个命令行实用程序,它显示网络数据包在从源传输到目标主机时遇到的节点的 IP 地址。在 CP 上有一些文章展示了如何实现 tracert 功能。
- CTraceRoute v1.0 作者:P. J. Naughter。
- 使用原始套接字进行路由跟踪 作者:Babar Qaisrani
上述文章使用了 C++ 代码,而我需要一个适用于 .NET Framework 2.0 的 C# 版本。幸运的是,.NET Framework 2.0 有一个 Ping
组件,本文提供的代码就是利用这个组件来实现 tracert 功能的。
使用 Tracert 组件?
在项目中使用的最简单的方法是将该组件添加到 VS.NET 工具箱中,然后将其拖放到适当的设计器表面。如果您将 tracert 项目添加为另一个项目的引用,那么 Tracert
组件将自动出现在 VS.NET 工具箱中,如下面的屏幕截图所示。同时还展示了组件在设计器表面上放置后的样子。
将组件添加到设计器表面后,您需要设置该组件的属性。您需要设置的最重要的属性是 HostNameOrAddress
属性,该属性可以设置为主机名或 IP 地址。
tracert.HostNameOrAddress = "www.codeproject.com"; //Use the host name
tracert.HostNameOrAddress = "208.90.12.1"; //Use the IPAddress
接下来,您需要为该组件公开的事件分配事件处理程序。这是因为该组件是异步工作的。您可以使用两个事件:RouteNodeFound
和 Done
。您可以处理 RouteNodeFound
事件来查找中间节点(当它们被找到时)。
tracert.RouteNodeFound +=
new EventHandler<RouteNodeFoundEventArgs>(tracert_RouteNodeFound);
在下面的代码片段中,节点 IP 地址在找到时被添加到列表视图中。
private void tracert_RouteNodeFound(object sender, RouteNodeFoundEventArgs e)
{
ListViewItem item = routeList.Items.Add(e.Node.Address.ToString());
.
.
}
最后,您需要处理 Done
事件来查找跟踪过程是否已完成。
tracert.Done += new System.EventHandler(this.tracert_Done);
private void tracert_Done(object sender, EventArgs e)
{
//Access all the nodes using tracert.Nodes proerty
.
.
//IP Address of the ith node
IPAddress address = tracert.Nodes[i].Address;
}
最后,可以通过调用 Trace
方法来启动实际的跟踪。
tracert.Trace();
这是使用该组件最简单、最常见的方式。您可以根据下一节中的描述设置其他属性来进一步控制组件的行为。
Tracert 组件参考
该组件具有如下公共属性。
属性名称 | 类型 | 描述 |
---|---|---|
MaxHops |
int |
到达目标所需的最大网络跃数。即使未到达目标,tracert 进程也会在达到最大跃数后中止。默认值为 30。 |
节点 |
Tracert 节点数组 |
返回路径中的所有节点。 |
HostNameOrAddress |
字符串 |
主机名,例如 www.codeproject.com/,或 IP 地址字符串,例如 209.12.1.99。 |
Timeout |
int |
这是在移至路径中的下一个节点之前,等待特定节点响应的时间(以毫秒为单位)。默认值为 5000。 |
IsDone |
bool |
如果为 true ,则表示已到达目标,或已达到最大跃数。 |
此控件的方法如下:
方法名称 | 返回值 | 描述 |
---|---|---|
Trace |
void |
此方法启动实际的路由跟踪。调用此方法之前,必须设置 HostNameOrAddress 属性。此方法是异步工作的。 |
事件如下:
事件名称 | 描述 |
---|---|
完成 |
当路由跟踪完成时,会触发此事件。这可能发生在到达目标主机时,或者达到最大跃数时。 |
RouteNodeFound |
当在路径中找到中间节点时,会触发此事件。提供给事件处理程序的 RouteNodeFoundEventArgs 参数包含有关该节点的其他信息。 |
RouteNodeFoundEventArgs 参考
类型为 RouteNodeFoundEventArgs
的参数会传递给 RouteNodeFound
事件的事件处理程序。该类的属性如下:
属性名称 | 类型 | 描述 |
---|---|---|
节点 |
TracertNode |
表示在迭代中找到的节点。 |
TracertNode 参考
TracertNode
类用于封装路径中的一个节点;它具有以下属性:
属性名称 | 类型 | 描述 |
---|---|---|
地址 |
IPAddress |
表示节点的 IP 地址。如果发生错误或超时,地址将等于 IPAddress.Any (0.0.0.0)。 |
RoundTripTime |
int |
从将数据包发送到节点到发送方收到确认之间的时间间隔(以毫秒为单位)。 |
状态 |
IPStatus |
发送到节点的 ping 请求的状态。可以是 IPStatus.Success 或 IPStatus.Timeout 。 |
Tracert 如何工作?
当您向目标发送 Ping 请求时,可以选择指定 TTL(生存时间) 值。当网络数据包从一个中间主机传输到另一个主机时,其 TTL 值会减一。当 TTL 值变为零时,发送方会收到一个错误确认 - TTL 已超出。发送方可以从收到该确认的主机中找出其 IP 地址。因此,如果您将 TTL 值设置为 1 并向目标发送数据包,则数据包将由路径中的第一个节点返回。然后,如果您将 TTL 值设置为 2 并再次发送数据包,则它将由路径中的第二个主机返回,依此类推。您可以继续此递增 TTL 值的过程,直到到达目标或达到最大跃数。下图显示了一个示例网络路径和网络数据包的行为。
图中的箭头显示了数据包的方向。粗线显示了数据包遍历的路径。虚线表示如果 TTL 没有过期,网络数据包可能会遍历该路径。现在我们已经对 Tracert
的工作原理有了简要的了解,让我们看看如何在代码中实现它。
在代码中实现 Tracert
我们将使用 .NET Framework 的 Ping
类来实现 tracert 功能。让我们直接从代码片段开始。
_ping = new Ping();
_ping.PingCompleted += new PingCompletedEventHandler(OnPingCompleted);
_options = new PingOptions(1, true);
_ping.SendAsync(_destination, _timeout, Tracert.Buffer, _options, null);
Ping
类可以使用 Send
方法同步发送 ping 请求,或者使用 SendAsync
方法异步发送。当使用 SendAsync
方法时,会触发 PingCompleted
事件来通知请求的完成。SendAsync
方法有许多不同的重载。在上述代码使用的重载中,参数如下:
参数 | 描述 |
---|---|
_destination |
这是最终目标的 IP 地址。 |
_timeout |
等待网络请求完成的时间(以毫秒为单位)。 |
Tracert.Buffer |
要作为请求发送的字节缓冲区。这取自一个静态变量。在我们的示例中,该缓冲区是一个 32 字节数组,填充了字符 A 的 ASCII 码。 |
_options |
一个 |
null (最后一个参数) |
最后一个参数是一个用户定义的对象,该对象在 |
一旦请求完成(无论成功还是不成功),就会调用 PingCompleted
事件的处理程序。在本例中,它是 OnPingCompleted
方法。
void OnPingCompleted(object sender, PingCompletedEventArgs e)
{
ProcessNode(e.Reply.Address, e.Reply.Status);
if (!this.IsDone)
{
lock (this)
{
//If the object is disposed the _ping
//member variable is set to null
if (_ping == null)
{
ProcessNode(_destination, IPStatus.Unknown);
}
else
{
_options.Ttl += 1;
ping.SendAsync(_destination, _timeout,
Tracert.Buffer, _options, null);
}
}
}
}
OnPingCompleted
函数带有两个参数。第一个参数是 sender
对象,它将是调用 SendAsync
方法的原始 Ping
对象。参数 e
是 PingCompletedEventArgs
类型,提供了有关事件的更多信息。PingCompletedEventArgs
的重要成员是 Reply
成员。此成员提供了数据包超时的节点 IP 地址和状态值。当数据包未成功到达目标时,状态值设置为 IPStatus.TtlExpired
;否则,它将设置为 IPStatus.Success
。我们在 ProcessNode
方法中处理中间节点。
protected void ProcessNode(IPAddress address,
IPStatus status)
{
long roundTripTime = 0;
if (status == IPStatus.TtlExpired ||
status == IPStatus.Success)
{
Ping pingIntermediate = new Ping();
try
{
//Compute roundtrip time to the address by pinging it
PingReply reply = pingIntermediate.Send(
address,
_timeout);
roundTripTime = reply.RoundtripTime;
status = reply.Status;
}
catch (PingException e)
{
//Do nothing
System.Diagnostics.Trace.WriteLine(e);
}
finally
{
pingIntermediate.Dispose();
}
}
TracertNode node = new TracertNode(address, roundTripTime,
status);
lock (_nodes)
{
_nodes.Add(node);
}
if (RouteNodeFound != null)
RouteNodeFound(this,
new RouteNodeFoundEventArgs(node, this.IsDone));
this.IsDone = address.Equals(_destination);
lock (_nodes)
{
if (!this.IsDone && _nodes.Count >= _maxHops - 1)
ProcessNode(_destination, IPStatus.Success);
}
}
在 ProcessNode
方法中,我们再次 ping 该节点。这是因为我们想获取到目标的往返时间。PingReply
类确实返回往返时间,但仅当数据包成功到达目标时才会返回。一旦获得往返时间,我们就会触发 RouteNodeFound
事件。接下来,我们将目标节点添加到节点列表中。最后,我们检查是否已到达最终目标。如果为真,我们将 IsDone
属性设置为 true
。简而言之,这就是该组件的工作方式。
历史
- 2006 年 6 月 25 日:初始提交。