我想(近)实时地逐行读取tcpdump子进程的输出,但是我需要选择评估管道是否为空(因此队列)的选项。线程等待0.5秒,获取所有排队的输出行,对其进行处理(例如,平均分配包超过0.5秒),然后返回内容。
最小无效示例:
millis = lambda: int(round(time.time() * 1000))
def enqueue_output(out, queue):
for line in iter(out.readline, b''):
print(millis())
print(line)
queue.put(line)
out.close()
def infiniteloop1():
p = Popen( [ 'sudo', 'tcpdump', '-i', 'wlan0', '-nn', '-s0', '-q', '-l', '-p', '-S' ], stdout=subprocess.PIPE, stderr=STDOUT)
q = Queue()
t = Thread(target=enqueue_output, args=(p.stdout, q))
t.daemon = True # thread dies with the program
t.start()
while True:
while True:
# read line without blocking
try:
row = q.get_nowait() # or q.get(timeout=.1)
except Empty:
print('empty')
break
else:
pass
time.sleep(0.5)
thread1 = threading.Thread(target=infiniteloop1)
thread1.daemon = True
thread1.start()
捕获连续包流时的输出:
[...]
1552905183422
10:33:03.334167 IP 192.168.1.2.36189 > a.b.c.d.443: tcp 437
1552905183422
10:33:03.357215 IP a.b.c.d.443 > 192.168.1.2.36189: tcp 0
1552905183423
10:33:03.385145 IP 192.168.1.2.36189 > a.b.c.d.443: tcp 437
empty
empty
1552905184438
10:33:03.408408 IP a.b.c.d.443 > 192.168.1.2.36189: tcp 0
1552905184439
10:33:03.428045 IP 192.168.1.2.36189 > a.b.c.d.443: tcp 437
1552905184439
10:33:03.451235 IP a.b.c.d.443 > 192.168.1.2.36189: tcp 0
[...]
注意两个连续的“空”。 tcpdump在10:33:03.385145捕获了第一个“空”之前的最后一个数据包,并在1552905183423处将其传递到队列,这花费了38 ms。在两个“空”之间,没有数据包被传递到队列中。在第二个“空”之后的第一个包在10:33:03.408408处捕获并交付1552905184438,在前一个数据包之后1秒交付,但在“空”之间捕获。为什么没有在“空”之间传递?这种情况很少发生,但是每隔一秒钟弹出队列就会导致没有包裹交付,为什么?
答案 0 :(得分:1)
第二个“空”之后的第一个包裹在 10:33:03.408408并交付1552905184438,交付1秒 在前一个数据包之后但在“空”之间捕获。
给出您的代码,仅当for line in iter(out.readline, b'')
中的迭代器返回一个新项目时才计算并打印系统时间戳,所以这似乎是延迟的来源。
我怀疑stdio缓冲是罪魁祸首。在Linux(即libc / glibc)上,如果STDOUT描述符引用了TTY,则启用行缓冲。如果它引用其他内容(例如管道),则STDOUT描述符已完全缓冲;在调用写入系统调用之前,您的进程需要填充4096字节(Linux上为默认值)。
根据此处显示的输出进行非常粗略的计算,您的子进程似乎每〜0.025秒生成〜65个字节。给定4kB缓冲区,将需要约1.625秒的时间将其填满并触发刷新/写入。
从该subprocess.PIPE
中读取并将输出发送到主进程的stdout所花费的时间要少得多,因此您看到的是tcpdump
输出的突发,打印间隔约为25ms(从stdout迭代器)在几微秒内,随后您的程序将等待直到刷新下一个4kB。
如果可以安装第三方软件包(并使用Python> = 2.7),则可能需要查看pexpect。该程序包的子程序连接到PTY,使系统将它们视为交互式程序,因此其stdout描述符是行缓冲的。