通过Popen帮助实现ping
我正在开发一个软件,用来监控与不同地点的通信。这个原理很简单:每秒发送一次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 个回答
这看起来是个不错的起点:
http://pypi.python.org/pypi/ping/0.1
当然,现在你做的事情到底有没有问题还不太清楚。因为ping的时间是从ping命令的输出中解析出来的,所以我们不能说是因为启动进程的开销导致的问题。
试着在你的 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 输出中的正确值吗?