通过Popen帮助实现ping

0 投票
2 回答
2888 浏览
提问于 2025-04-16 12:35

我正在开发一个软件,用来监控与不同地点的通信。这个原理很简单:每秒发送一次ping请求,并实时显示结果(比如延迟时间,丢包率等)。

值得一提的是,我是在Linux系统上运行这个软件的,所以为了能从我的软件中发送ping请求,我选择了使用subprocess.Popen的方法,因为打开网络连接需要以root用户身份登录。而我不想给每个人都提供服务器的root权限……

下面是负责发送ping请求的类:


 class WorkerThread(QThread):
  def __init__(self,receiver,sitename):
    QThread.__init__(self)

    global time_manager
    time_manager[sitename] = [time.time(),0,0] #for statistic purpeses

    self.stopped = 0
    self.receiver = receiver
    self.sitename = sitename


  def run(self):
    icmp_count = 0
    ping_result = ""
    packeloss_result = ""

    while not self.stopped:
       data = subprocess.Popen("ping -c1 "+str(sites[self.sitename]),shell = True,stdout=subprocess.PIPE)
       data.wait()
       time_manager[self.sitename][1] +=1 #counts the icmps sent 
       bufferdata = data.stdout.read() 
       ping_result = ms_pat.findall(bufferdata)
       packeloss_result = packetloss_pat.findall(bufferdata)

       if ping_result:
         ping_ms = ping_result[0][0]
       if packeloss_result:
         time_manager[self.sitename][2] +=1        
         ping_ms = "-1"

       ms_count[self.sitename].append(float(ping_ms))
       time.sleep(1)
       event = QCustomEvent(12345)
       event.setData(self.sitename+ping_ms)
       QApplication.postEvent(self.receiver, event)

  def stop(self):
    self.stopped = 1

我使用线程是因为有时候我需要同时对多个网站进行ping测试。

我的问题是:在运行时,我能准确得到延迟时间的结果,但每隔几次ping请求就会出现一个不准确的结果,延迟时间比实际应该的要高。

我知道这些结果不准确,因为我同时在控制台中手动运行ping命令,而那里没有出现这种延迟的尖峰。

举个例子:

ping_ms = 20.0

ping_ms = 21.31

ping_ms = 23.23

ping_ms = 80.2

ping_ms = 23.23

ping_ms = 24.2

我不明白为什么会这样。也许我需要以不同的方式编写代码。如果有人能帮我,那将非常感谢。

谢谢。

我已经找到了问题所在:

似乎问题不在代码上,而是在操作系统或ping命令本身。当我在控制台中每秒手动运行命令:“ping -c1 xxx.xxx.xxx.xxx”时,经过几次尝试,我也会得到同样的结果,出现奇怪的延迟尖峰。但是如果我使用连续的ping命令“ping xxx.xxx.xxx.xxx”,就没有尖峰。

有没有可能通过脚本使用Popen来运行连续的ping命令并读取结果呢?

2 个回答

1

这看起来是个不错的起点:

http://pypi.python.org/pypi/ping/0.1

当然,现在你做的事情到底有没有问题还不太清楚。因为ping的时间是从ping命令的输出中解析出来的,所以我们不能说是因为启动进程的开销导致的问题。

0

试着在你的 Popen 调用中加上 close_fds=True(具体讨论可以看 这里)。如果你在其他线程中还有其他管道打开,可能会导致它们之间有一些交互(强制线程按照特定顺序执行)。建议使用 Popen.communicate() 来处理,而不是直接从进程的标准输出流中读取。话虽如此,你的 ping 实现根据输出缓冲的好坏返回不同的读数的可能性似乎不大。

从更高的层面来看,你需要记住,ping 的时间数据本身就是不可靠的。如果你无法解决这个波动,并且你确信这是由于 Popen 调用造成的(而不是随机噪声),那么对数据进行后处理是合理的。比如,你可以每次收集 5 个数据点,然后取中位数。(注意,中位数对异常值的敏感度比平均值低)。这和 ping 本身的做法没有什么区别。

更新 26/02

很遗憾听到上面的建议没有帮助。还有几个其他的想法:

1. 如果我们能多了解一下你是如何在命令行中运行 ping 的,那会很有帮助。在 Python 中,你使用 -c1 来限制 ping 只尝试一次。我猜在命令行中,你只是运行 ping hostname 并实时查看结果——这样对吗?如果是的话,请尝试运行以下的 bash 脚本:

#! /bin/bash

for i in {1..50}
do
   ping -c1 somehostname
   # maybe 'sleep 1' here
done

然后仔细查看结果。可能这些波动是你调用 ping 的方式造成的。

2. 你的示例代码中没有正则表达式的定义。你确定它们能准确捕捉到 ping 输出中的正确值吗?

撰写回答