笔者近期做的服务器监控UWP小程序,结果发现在UWP程序的.NET命名空间下取消了Ping的部分,看了诸多资料,觉得可能是.NET 4.5 Framework 暂时不支持ICMP。于是笔者想着通过Socket(命名空间保留了的部分)走上层模拟Ping请求,不过这种方式并不能完全达到效果,原因在于Socket建立连接是需要终结点的,即IP+端口号,而实际上ICMP请求是不需要端口号的,直接通过发送ICMP询问报文获取服务器状态即可。
摘记笔者实现的简易Ping方法:
/// <summary>
/// 以数据报的消息格式请求连接服务器
/// </summary>
/// <param name="desEndPoing">终结点</param>
/// <returns></returns>
public static async Task<Tuple<string, string, string, string>> DatagramSocketConnect(IPEndPoint desEndPoing)
{
// 请求状态
string requestStatus = "";
// 请求耗时
string requestCost = "";
// 请求结果
string color = "";
// 请求的额外信息
string others = "";
// 用来记录请求耗时
var s = new System.Diagnostics.Stopwatch();
s.Start();
try
{
// 使用数据报进行消息传输
using (var UdpClient = new DatagramSocket())
{
// 超时控制
CancellationTokenSource cts = new CancellationTokenSource();
cts.CancelAfter(1000);
// HostName 构造需要一个主机名orIP地址(不带http!)
// 异步建立连接
await UdpClient.ConnectAsync(
new Windows.Networking.HostName(desEndPoing.Address.ToString()),
desEndPoing.Port.ToString())
// 作为Task任务,添加超时令牌
.AsTask(cts.Token);
// 停表
s.Stop();
var remoteIp = UdpClient.Information.RemoteAddress;
Debug.WriteLine(String.Format("Success, remote server contacted at IP address {0},and the connecting work cost {1} millsseconds!",
remoteIp, s.ElapsedMilliseconds));
#region 修改返回数据
requestStatus = "200";
requestCost = s.ElapsedMilliseconds.ToString();
color = "green";
others = String.Format("Success, remote server contacted at IP address {0},and the connecting work cost {1} millsseconds!",
remoteIp, s.ElapsedMilliseconds);
#endregion
// 释放连接
UdpClient.Dispose();
return new Tuple<string, string, string, string>(requestStatus, requestCost, color, others);
}
}
// 捕获自定义超时异常
catch (TaskCanceledException)
{
#region 修改返回数据
requestStatus = "1000";
requestCost = s.ElapsedMilliseconds.ToString();
color = "orange";
others = "Error: Timeout when connecting (check hostname and port)";
#endregion
return new Tuple<string, string, string, string>(requestStatus, requestCost, color, others);
}
// 捕获常见的异常
catch (Exception ex)
{
s.Stop();
// 查不到对应HostName的服务器
if (ex.HResult == -2147013895)
{
#region 修改返回数据
requestStatus = "0";
requestCost = s.ElapsedMilliseconds.ToString();
color = "red";
others = "Error: No such host is known";
#endregion
Debug.WriteLine("Error: No such host is known");
}
// 请求超时
else if (ex.HResult == -2147014836)
{
#region 修改返回数据
requestStatus = "1000";
requestCost = s.ElapsedMilliseconds.ToString();
color = "orange";
others = "Error: Timeout when connecting (check hostname and port)";
#endregion
Debug.WriteLine("Error: Timeout when connecting (check hostname and port)");
}
// 其他异常
else
{
#region 修改返回数据
requestStatus = "0";
requestCost = s.ElapsedMilliseconds.ToString();
color = "red";
others = "Error: Exception returned from network stack: " + ex.Message;
#endregion
Debug.WriteLine("Error: Exception returned from network stack: " + ex.Message);
}
return new Tuple<string, string, string, string>(requestStatus, requestCost, color, others);
}
}
上述实现方式中几个重要需要注意的点:
- 方法体异步(async + await)
- 异常捕获处理(可能还存在笔者未捕获到的异常)
- 笔者代码中加入了人为超时判断,实际上使用DatagramSocket会自己有超时判断,大概是5s左右
上面笔者是用的DatagramSocket实现的,实际上还有很多种Socket也可以实现:MessageWebSocket、StreamSocket等等
OK,就先记录到这!