客户端/服务器配置中的嗅探器/监视器以及Python中的ZMQ

客户端/服务器配置中的嗅探器/监视器以及Python中的ZMQ,python,sockets,zeromq,pyzmq,Python,Sockets,Zeromq,Pyzmq,我通过ZeroMQ实现了一个客户机/服务器,我想添加一个嗅探器/监视器来捕获两者之间的通信 client <---------> server (REQ) | (REP) | | v sniffer

我通过ZeroMQ实现了一个客户机/服务器,我想添加一个嗅探器/监视器来捕获两者之间的通信

              client <---------> server
              (REQ)       |         (REP)
                          | 
                          |
                          v
                        sniffer  <-this is what I want to add
客户端服务器
(要求)|(代表)
| 
|
v

嗅探对于嗅探,我们需要一些中间部分

zmq提供了两种选择

  • 编写您自己的程序,一边接受请求,一边发送请求,一边得到响应,一边发送给原始请求者,然后向您报告此流量
  • 使用zmq.proxy-但是,这需要最新版本的
    libzmq
    (zmq.zmq\u version\u info()>=3),这在我的Ubuntu 14.04上都不可用,所以我跳过了这个
  • 使用MonitoredQueue-这可能是您想要的。这提供了一个在前端和后端之间交换消息的循环,同时将消息发布/推送/发送到另一个套接字
计划 此解决方案基于

服务器绑定到端口5555 服务器将绑定到端口5555。与其他示例不同,我将把您的服务器保持为固定的部分,而不会将其更改为连接到monitoredqueue。但是,这样的交换不是问题,也不会产生任何问题(只要您正确调整MonitoredQueue)

MonitoredQueue绑定到端口4444,连接到端口5555,在端口7777上发布流量 MonitoredQueue位于客户端和服务器之间。它侦听端口4444,将请求发送到服务器,并将响应发送回客户端。同时,任何经过的消息都将在PUB套接字上以相应的前缀“in”或“out”发布。我们将在后面看到,它们不仅包含前缀和请求/响应,还包含客户端的标识

连接到端口4444的客户端 客户端可以直接连接到端口5555上的服务器,但这不允许我们嗅探流量。出于这个原因,我们将把客户端连接到端口4444,其中MonitoredQueue正在等待服务器和嗅探

您将看到,客户端和服务器不必更改代码行即可参与此交换

实码
server.py
在我们的例子中,服务器需要一个可以转换为整数的字符串,并返回一个值加倍的字符串

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
我们的客户端将在本地主机上的端口4444上尝试5次,询问一些结果

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()
有关套接字类型和别名的注意事项:

  • 路由器过去被称为zmq.XREP
  • zmq.DEALER过去被称为zmq.XREQ
  • 这些别名仍然有效
MonitoredQueue将在端口7777上的zmq.PUB套接字上发布通过的每条消息。这些消息将以“in”和“out”作为前缀,还将包含一个带有标识字符串的帧。此标识字符串由路由器套接字分配,在交换过程中,它对于所有连接的客户端都是唯一的。该标识是所谓信封的一部分,来自以空框分隔的请求/回复消息(稍后将看到)

monitorclient.py
这个监控客户端只是为了展示如何获取嗅探信息

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()
它订阅由monitor(MonitoredQueue)提供服务的端口7777并将其打印出来。使用多部分消息很重要,否则我们将丢失一些信息

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 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
启动客户端,读取嗅探到的消息

$ python monitorclient.py
最后,启动客户端,尝试从MonitoredQueue代理的服务器获取一些响应

$ python monitor.py
$ 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个响应的打印输出

每个都有一个结构
[前缀、标识、空帧、消息]
其中

  • 前缀
    为“in”或“out”
  • identity
    是由MonitoredQueues分配给特定客户端的字符串。每次客户端连接时,此标识可能会更改。作为奖励,我们可以连接多个客户端,并且仍然有机会区分不同的客户端。如果您需要特定的客户端标识,请参阅带有
    socket.setsockopt(zmq.IDENTITY,“client\u id\u abc”)
    client.py
    中的注释行。如果取消注释,您将看到
    “client\u id\u abc”
    作为您客户的身份
  • emptyframe
    被视为
    '
    ,并从消息数据中界定信封
  • 消息
    是客户端询问或服务器回复的内容
结论
  • 嗅探工作正常,PyZMQ已经为此提供了设备MonitoredQueue
  • 使用
    zmq.PUB
    嗅探将不会阻止任何通信,您可以简单地忽略嗅探的数据,所有这些都可以工作
  • 对于生产,将MonitoredQueue作为系统的固定部分是可行的,这样就可以绑定到已知的IP地址和端口。这需要对服务器进行更改,服务器必须连接(而不是当前绑定)。这样的更改是微不足道的,不会影响其余的代码和行为。如果只有一个端点要监视,还可以将监视器嵌入服务器(这需要两个线程,一个用于服务器,另一个用于监视器)
  • zmq
    对于这类任务来说是非常棒的“乐高”
什么样的插座?在酒吧