我通过ZeroMQ实现了一个客户端/服务器,我想添加一个嗅探器/监视器来捕获两者之间的通信。
client <---------> server
(REQ) | (REP)
|
|
v
sniffer <-this is what I want to add
如果客户端/服务器通过套接字5555进行通信,那么,我怎么能添加一个嗅探器来监听同一个套接字?有没有办法区分哪个消息来自客户端,哪个消息来自服务器? 有人可以分享经验吗?
根据Jan&#39;编辑答案
配置将变为:
client [REQ]----->[ROUTER:4444] monitor [DEALER]------->[REP:5555] server
[PUB:7777]
^
|
|
|
|
[SUB]
monitorclient(sniffer) <-this is what I want to add
箭头显示连接方向(前往绑定端口)。
消息正在流动:
client
- &gt; monitor
- &gt; server
- &gt; monitor
- &gt; client
monitor
- &gt; monitorclient
有一张更好的图片here。
答案 0 :(得分:10)
对于嗅探,我们需要一些中间部分。
zmq提供了几个选项
libzmq
(zmq.zmq_version_info()&gt; = 3),这在我的Ubuntu 14.04上目前甚至不可用,所以我跳过这个。此解决方案基于MonitoredQueue example from pyzmq doc
服务器将被绑定到端口5555.与其他示例不同,我将您的服务器保持为固定部分,而不是将其更改为连接到MontitoredQueue。但是,这样的交换不是问题,也不会产生任何问题(只要你正确调整MonitoredQueue)。
MonitoredQueue位于客户端和服务器之间。它侦听端口4444,向服务器发送请求并将响应发送回客户端。同时,任何传递的消息都将以相应的前缀&#34;在&#34;中发布。或&#34; out&#34;在PUB插座上。我们稍后会看到,这些不仅包含前缀和请求/响应,还包含客户端的身份。
客户端可以通过端口5555直接连接到服务器,但这不允许我们嗅探流量。出于这个原因,我们将客户端连接到端口4444,MonitoredQueue等待服务器和嗅探。
您将看到,客户端和服务器不必更改一行代码即可参与此交换。
server.py
在我们的例子中,服务器需要一个可以转换为整数的字符串,并返回一个doubled值的字符串。
import zmq
def double_server(server_url="tcp://*:5555"):
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind(server_url)
print "server started..."
while True:
req = socket.recv()
print "server received request", req
result = str(2*int(req))
socket.send(result)
print "server replied with", result
if __name__ == "__main__":
double_server()
client.py
我们的客户将尝试5次在localhost上的端口4444上询问一些结果。
import zmq
def client(server_url="tcp://localhost:4444"):
context = zmq.Context()
socket = context.socket(zmq.REQ)
# socket.setsockopt(zmq.IDENTITY, "client_id_abc") # see Conclusions
socket.connect(server_url)
for i in range(5):
print "request", i
socket.send(str(i))
res = socket.recv()
print i, "result: ", res
if __name__ == "__main__":
client()
您现在可以尝试直接连接到端口5555以查看它是否有效,但是对于我们的嗅探,它必须与MonitoredQueue交谈。
monitor.py
这就是所有的魔力。 pyzmq
已经提供了设备MonitoredQueue
,因此我们可以简单地使用它。
import zmq
from zmq.devices.monitoredqueuedevice import MonitoredQueue
from zmq.utils.strtypes import asbytes
def monitoredqueue(frontend_url="tcp://*:4444", server_url="tcp://localhost:5555", capture_url="tcp://*:7777"):
mondev = MonitoredQueue(zmq.ROUTER, zmq.DEALER, zmq.PUB, asbytes("in"), asbytes("out"))
mondev.bind_in(frontend_url)
mondev.connect_out(server_url)
mondev.bind_mon(capture_url)
mondev.setsockopt_in(zmq.HWM, 1)
mondev.start()
print "monitored queue started"
if __name__ == "__main__":
monitoredqueue()
关于套接字类型和别名的注意事项:
MonitoredQueue将在端口7777上发布zmq.PUB套接字上传递的每条消息。这些消息的前缀为&#34; in&#34;和&#34; out&#34;并且还将包含一个带有标识字符串的框架。此标识字符串由ROUTER套接字分配,在交换期间,它对所有连接的客户端都是唯一的。这个身份是所谓的信封的一部分,来自空框架分隔的请求/回复消息(很快就会看到)。
monitorclient.py
此监控客户端仅用于显示如何获取嗅探信息。
它订阅了端口7777,由monitor(MonitoredQueue)提供并打印出来。消费多部分消息很重要,否则我们会遗漏一些信息。
import zmq
def monitorclient(server_url="tcp://localhost:7777"):
context = zmq.Context()
socket = context.socket(zmq.SUB)
socket.connect(server_url)
socket.setsockopt(zmq.SUBSCRIBE, "")
print "started monitoring client"
while True:
res = socket.recv_multipart()
print res
if __name__ == "__main__":
monitorclient()
我们需要打开4个控制台,每个控制台都会启动一个python脚本
首先启动服务器:
$ python server.py
启动MonitoredQueue
$ python monitor.py
启动客户端,阅读嗅探消息
$ python monitorclient.py
最后,启动客户端尝试从MonitoredQueue代理的服务器获取一些响应
$ python client.py
request 0
0 result: 0
request 1
1 result: 2
request 2
2 result: 4
request 3
3 result: 6
request 4
4 result: 8
结果与预期一致。
现在检查server.py输出:
$ python server.py
server received request 0
server replied with 0
server received request 1
server replied with 2
server received request 2
server replied with 4
server received request 3
server replied with 6
server received request 4
server replied with 8
毫不奇怪,一切顺利。
我们的monitor.py
不打印任何内容,我们必须检查monitorclient.py
的输出
$ python monitorclient.py
started monitoring client
['in', '\x00\xc4\x84\x1c\xf2\xc2.@\xd3\x86cN\x0e\x06\x7f\xaf\x0b', '', '0']
['out', '\x00\xc4\x84\x1c\xf2\xc2.@\xd3\x86cN\x0e\x06\x7f\xaf\x0b', '', '0']
['in', '\x00\xc4\x84\x1c\xf2\xc2.@\xd3\x86cN\x0e\x06\x7f\xaf\x0b', '', '1']
['out', '\x00\xc4\x84\x1c\xf2\xc2.@\xd3\x86cN\x0e\x06\x7f\xaf\x0b', '', '2']
['in', '\x00\xc4\x84\x1c\xf2\xc2.@\xd3\x86cN\x0e\x06\x7f\xaf\x0b', '', '2']
['out', '\x00\xc4\x84\x1c\xf2\xc2.@\xd3\x86cN\x0e\x06\x7f\xaf\x0b', '', '4']
['in', '\x00\xc4\x84\x1c\xf2\xc2.@\xd3\x86cN\x0e\x06\x7f\xaf\x0b', '', '3']
['out', '\x00\xc4\x84\x1c\xf2\xc2.@\xd3\x86cN\x0e\x06\x7f\xaf\x0b', '', '6']
['in', '\x00\xc4\x84\x1c\xf2\xc2.@\xd3\x86cN\x0e\x06\x7f\xaf\x0b', '', '4']
['out', '\x00\xc4\x84\x1c\xf2\xc2.@\xd3\x86cN\x0e\x06\x7f\xaf\x0b', '', '8']
在这里,您可以看到所有10条消息的打印输出,5条请求,5条响应。
每个都有一个结构[prefix, identity, emptyframe, message]
其中
prefix
是&#34; in&#34;或&#34; out&#34; identity
是由MonitoredQueues分配给特定客户端的字符串。每次客户端连接时,此标识可能会更改。作为奖励,我们可能连接多个客户端,仍然有机会区分不同的客户端。如果您需要特定的客户身份,请参阅client.py
中带有socket.setsockopt(zmq.IDENTITY, "client_id_abc")
的注释行。如果您取消注释,则会看到"client_id_abc"
作为您客户的身份。emptyframe
被视为''
,并且正在从邮件数据中分隔信封。message
是客户询问的内容或服务器回复的内容。zmq.PUB
嗅探不会阻止任何通信,您可能只是忽略了嗅探数据,一切都会有效。zmq
很棒&#34; Lego&#34;对于这类任务。