如何用C#实现类Linux的UDP Traceroute排查游戏服务器延迟?
《行星边际2》路由排查工具开发问题求助
近期我和其他《行星边际2》玩家都遇到高延迟问题,需要排查路由节点故障。Windows自带的tracert和Ping工具只能获取前几个节点,后续返回* * *,推测是服务器防火墙拦截导致。但Linux下的UDP traceroute命令(traceroute -U -p 20105 69.174.216.21)可以成功获取完整路由:
1 Desktop (my ip) 0.340 ms 2 router (router ip) 3.305 ms 3 isp (isp ip) 8.955 ms 4 62.214.42.189 (62.214.42.189) 10.675 ms 5 62.67.18.94 (62.67.18.94) 15.491 ms 6 * * * 7 * * * 8 * * * 9 * * * 10 8.227.56.190 (8.227.56.190) 99.620 ms 11 69.174.217.5 (69.174.217.5) 98.522 ms 12 69.174.216.21 (69.174.216.21) 103.844 ms
因为不是所有玩家都熟悉Linux,我想开发一个C#版本的工具供社区使用,但遇到了以下问题:
- ICMP实现问题:只能遍历前5个TTL节点,后续节点多次尝试均失败,无法到达目标IP,结果如下:
1 Desktop IP TtlExpired 2 Router IP TtlExpired 3 ISP IP TtlExpired 4 62.214.42.189 TtlExpired 5 62.67.18.94 TtlExpired 6 failed after 3 attempts. 7 failed after 3 attempts. 8 failed after 3 attempts. 9 failed after 3 attempts. 10 8.227.56.190 DestinationNetworkUnreachable 11 failed after 3 attempts. 12 failed after 3 attempts.
- UDP实现问题:前6个节点触发SocketException/AggregateException(连接因保活检测终止),仅能识别目标IP,无法获取中间节点IP,结果如下:
Traceroute[0]: - Keepalive blocked Traceroute[1]: - Keepalive blocked Traceroute[2]: - Keepalive blocked Traceroute[3]: - Keepalive blocked Traceroute[4]: - Keepalive blocked Traceroute[5]: - No response received Traceroute[6]: - No response received Traceroute[7]: - No response received Traceroute[8]: - No response received Traceroute[9]: - No response received Traceroute[10]: - Keepalive blocked Traceroute[11]: - Keepalive blocked Traceroute[11]: 69.174.216.21 - Target reached
- TCP实现完全失败
我试过Stack Overflow上的相关示例但没用,了解到可能需要用原始套接字监听ICMP TIME_EXCEEDED,但不知道具体怎么实现。附上现有ICMP和UDP代码,请求指导如何修正代码,实现类似Linux的UDP Traceroute功能以获取完整路由节点。
ICMP代码
var limit = 15; var buffer = new byte[32]; var pingOpts = new PingOptions(1, true); var ping = new Ping(); PingReply result = null; do { int retries = 0; pingOpts = new PingOptions(pingOpts.Ttl + 1, pingOpts.DontFragment); while (retries < 3) { result = ping.Send(ipAddress, 1000, buffer, pingOpts); if (result.Status != IPStatus.TimedOut) { yield return result; break; } retries++; } if (retries == 3) { Console.WriteLine($"TTL {pingOpts.Ttl} failed after 3 attempts."); } } while (result.Status != IPStatus.Success && pingOpts.Ttl < limit);
UDP代码
for (short ttl = 0; ttl <= 15; ttl++) { string message = $"Traceroute[{ttl}]: "; try { using (UdpClient udpClient = new UdpClient()) { udpClient.Client.ReceiveTimeout = 3000; udpClient.Client.Ttl = ttl; udpClient.DontFragment = true; udpClient.Send(new byte[1], 1, ipAddress, targetPort); Task<UdpReceiveResult> receiveTask = udpClient.ReceiveAsync(); if (await Task.WhenAny(receiveTask, Task.Delay(3000)) == receiveTask) { UdpReceiveResult result = receiveTask.Result; //crashes here because of keep-alive smth IPAddress senderIp = result.RemoteEndPoint.Address; if (senderIp.Equals(IPAddress.Parse(ipAddress))) { Console.WriteLine(message + ipAddress.ToString() + " - Target reached"); } else { Console.WriteLine(message + $" - Received response from {senderIp}"); } } else { Console.WriteLine(message + " - No response received"); } } } catch (SocketException ex) { if (ex.SocketErrorCode == SocketError.TimedOut) { Console.WriteLine(message + " - No response received"); } } catch (System.AggregateException ex) { Console.WriteLine(message + " - Keepalive blocked"); } }
内容的提问来源于stack exchange,提问作者Little Yuuta
相关产品推荐
相关产品推荐

