C#代码Ping IP结果与命令行不一致,原因何在?
问题描述
我编写了一段用于Ping IP并记录日志的C#代码:
private async ValueTask PingMachine() { var timesToPing = 4; var counter = 1; while (counter <= timesToPing) { var reply = await Pinger(); foreach (var r in reply) { TagService.log_PLC.AppendLine($" Pinged {TagService.IP_PLC_List} {counter} times time:{r.RoundtripTime} status: {r.Status.ToString()}"); } } counter++; } private async Task<IEnumerable<PingReply>> Pinger() { List<string> addresses = new List<string>(); if (Check_IP_Correct(TagService.IP_PLC_List) == true) { addresses.Add(TagService.IP_PLC_List); } var tasks = addresses.Select(async ip => await new Ping().SendPingAsync(ip, 1000)); var reply = await Task.WhenAll(tasks); return reply; }
测试在线IP时无异常,但测试某不可达IP时,日志显示:
Pinged 10.92.XXX.XX 1 times time:0 status: TimedOut Pinged 10.92.XXX.XX 2 times time:0 status: TimedOut Pinged 10.92.XXX.XX 3 times time:0 status: TimedOut Pinged 10.92.XXX.XX 4 times time:0 status: DestinationHostUnreachable
而在同一服务器使用命令行ping该IP时,每次均返回:
Reply from 100.72.XXX.XX: Destination host unreachable. Reply from 100.72.XXX.XX: Destination host unreachable. Reply from 100.72.XXX.XX: Destination host unreachable. Reply from 100.72.XXX.XX: Destination host unreachable.
我认为命令行的结果是正确的,请问为何C#代码返回的结果不同?可能存在什么问题?
原因分析
1. 超时时间设置不匹配
Windows命令行ping默认超时时间是4秒,而你的C#代码里设置的超时仅为1000毫秒(1秒)。当目标IP不可达时,路由设备返回的ICMP“目标不可达”包可能因网络延迟,没能在1秒内到达程序,导致前几次请求被判定为超时;后续请求时网络栈缓存了路由信息,错误包能更快返回,才会显示DestinationHostUnreachable。
2. ICMP响应处理逻辑差异
命令行ping会直接解析所有ICMP响应包,包括路由返回的错误包;而C#的Ping类底层实现和系统命令行的ICMP调用逻辑有区别,对错误包的优先级处理不同。前几次请求时,Ping类可能优先触发超时逻辑,直到网络栈明确返回不可达的错误状态,才会更新返回值。
3. 代码存在逻辑错误
你的PingMachine方法里,counter++写在了while循环的外面,这会导致循环条件counter <= timesToPing永远成立(counter始终为1),理论上会无限循环。但你的日志显示执行了4次,大概率是代码缩进错误,counter++应该放在循环内部。这个错误会导致重复发送Ping请求,而网络栈的路由状态逐渐更新后,才返回正确的不可达状态。
4. 网络栈缓存机制影响
第一次发送Ping请求后,系统网络栈需要时间缓存“目标不可达”的路由信息。前几次请求时缓存未生效,程序收到超时;后续请求时缓存生效,直接返回正确的错误状态。而命令行ping可能会利用已有的系统路由缓存,所以每次都能正确捕获到不可达的响应。
内容的提问来源于stack exchange,提问作者Mdarende

