简体   繁体   English

如何将Python ZMQ链接到标准UDP或TCP服务器(旧版,大约15年前使用C ++编写)

[英]How to link Python ZMQ to a standard UDP or TCP server (legacy, written using C++ ~ 15 years ago)

I need to communicate between a package written in Python (PsychoPy, for psychological / behavioral experiments) and a legacy piece of C++ software that can use UDP or TCP. 我需要在用Python编写的软件包(PsychoPy,用于心理/行为实验)与可以使用UDP或TCP的旧版C ++软件之间进行通信。 In particular, on the Python / PsychoPy side I need an asynchronous process like Matlab's pnet() that will poll a socket to see if it has any data to read, process the data if yes, or just move on if no. 特别是在Python / PsychoPy方面,我需要一个类似Matlab的pnet()的异步进程,该进程将轮询套接字以查看是否有任何要读取的数据,如果是,则处理数据,否则,继续进行下去。

ZMQ was recommended to me; 向我推荐了ZMQ; but all the example code I see using zmq_polling assumes that both the sending and receiving occur with ZMQ protocols. 但是我使用zmq_polling看到的所有示例代码都假定发送和接收都是通过ZMQ协议进行的。 Is there some simple Python ZMQ code that connects to a non-zmq TCP or UDP source, and does polling to check for the presence of data without getting hung up if there is no data to read? 是否有一些简单的Python ZMQ代码连接到非zmq TCP或UDP源,并且在没有数据可读取的情况下进行轮询以检查数据是否存在而不会挂断?

Thanks Aniruddha 谢谢阿尼鲁达(Aniruddha)

import zmq
import time


# Prepare our context and sockets
context = zmq.Context()

# Bind socket to local host
receiver = context.socket(zmq.PULL)
receiver.bind("tcp://129.236.162.112:55513")
#print( "Connected to server with port %s" % port_push)

# Initialize poll set
poller = zmq.Poller()
poller.register(receiver, zmq.POLLIN)



# Process messages from socket
while True:
    print('Entered the queue')
    try:
        socks = dict(poller.poll())
    except KeyboardInterrupt:
        break

    if receiver in socks:
        message = receiver.recv()
        # process task
        print(repr(message))


    else:
        print('Nothing to show')

    time.sleep(0.01)

I can send small TCP packets from the legacy C++ machine; 我可以从旧版C ++机器发送小的TCP数据包; they get sent out without any error messages, implying no problem. 它们被发送出去而没有任何错误消息,这意味着没有问题。 But nothing happens with this Python code 但是此Python代码没有任何反应

The above code enters the 'try' and just stays there. 上面的代码进入“ try”,并停留在那里。

How do I access error / status messages to debug? 如何访问错误/状态消息进行调试?

Thanks Aniruddha 谢谢阿尼鲁达(Aniruddha)

Welcome to the Zen-of-Zero : Yes, this is possible 欢迎来到零之禅:是的,这是可能的

Your post asks many questions at once. 您的帖子同时提出了许多问题。 Let's go from one to another. 让我们从一个到另一个。


Q1 : How do I access error / status messages to debug? 问题1如何访问错误/状态消息进行调试?

ZeroMQ documentation presents tools for this. ZeroMQ文档提供了用于此目的的工具。 C-side bindings have in common to receive an explicit return code, that may get inspected via assert() plus some more details could be retrieved from errno : C端绑定共有一个显式的返回码,可以通过assert()进行检查,还可以从errno检索更多详细信息:

void *ctx    = zmq_ctx_new();                     assert( ctx     && "EXC: Context failed to instantiate" );
void *socket = zmq_socket( ctx, ZMQ_STREAM );     assert( socket  && "EXC: Socket failed to instantiate" );
int   rc     = zmq_bind( socket, "tcp://*:8080" );assert( rc == 0 && "EXC: Bind failed to setup a Transport-Class" );

Q2+3 : Is there some simple Python ZMQ code that connects to a non-zmq TCP or UDP source (2) , and does polling (3) to check for the presence of data without getting hung up if there is no data to read? Q2 + 3是否有一些简单的Python ZMQ代码连接到非zmq TCP或UDP源(2) ,并进行轮询(3)以检查是否存在数据,如果没有要读取的数据就不会挂断?

For very this purpose (2) , ZeroMQ framework has been equipped somewhere about version 3.2+ with a STREAM Scalable Formal Communication Pattern Archetype. 为此(2) ,ZeroMQ框架在3.2+左右的某个版本中配备了STREAM可扩展正式通信模式原型。 If not sure, how ZeroMQ architecture uses Context, Context's Socket(s)'s Archetypes, Socket's Transport-Class AccessPoint(s), you may like a short read into "ZeroMQ Principles in less than Five Seconds " before diving into even further details about ZeroMQ 如果不确定,那么ZeroMQ架构如何使用Context,Context的Socket原型,Socket的传输类AccessPoint,您可能想 深入了解更多细节之前,先简短阅读不到5秒的 ZeroMQ 原理 ”。 关于ZeroMQ

A socket of type ZMQ_STREAM is used to send and receive TCP data from a non-ØMQ peer, when using the tcp:// transport. 使用tcp://传输时,类型为ZMQ_STREAM的套接字用于从非ØMQ对等方发送和接收TCP数据。 A ZMQ_STREAM socket can act as client and/or server, sending and/or receiving TCP data asynchronously. ZMQ_STREAM套接字可以充当客户端和/或服务器,以异步方式发送和/或接收TCP数据。

When receiving TCP data, a ZMQ_STREAM socket shall prepend a message part containing the identity of the originating peer to the message before passing it to the application. 当接收TCP数据时,在将消息传递给应用程序之前, ZMQ_STREAM套接字应在消息部分之前包含消息的原始对等方的身份。 Messages received are fair-queued from among all connected peers. 接收到的消息从所有连接的同级之间公平排队。

When sending TCP data, a ZMQ_STREAM socket shall remove the first part of the message and use it to determine the identity of the peer the message shall be routed to, and unroutable messages shall cause an EHOSTUNREACH or EAGAIN error. 发送TCP数据时, ZMQ_STREAM套接字应删除消息的第一部分,并使用它来确定消息应路由到的对等方的身份,并且不可路由的消息将导致EHOSTUNREACHEAGAIN错误。

To open a connection to a server, use the zmq_connect call, and then fetch the socket identity using the ZMQ_IDENTITY zmq_getsockopt call. 要打开与服务器的连接,请使用zmq_connect调用,然后使用ZMQ_IDENTITY zmq_getsockopt调用获取套接字标识。

To close a specific connection, send the identity frame followed by a zero-length message (see EXAMPLE section). 要关闭特定连接,请发送标识帧,然后发送零长度消息(请参见示例部分)。

When a connection is made, a zero-length message will be received by the application. 建立连接后,应用程序将收到零长度的消息。 Similarly, when the peer disconnects (or the connection is lost), a zero-length message will be received by the application. 同样,当对等方断开连接(或连接断开)时,应用程序将接收零长度的消息。

You must send one identity frame followed by one data frame. 您必须先发送一个标识帧,然后发送一个数据帧。 The ZMQ_SNDMORE flag is required for identity frames but is ignored on data frames. ZMQ_SNDMORE标志对于标识帧是必需的,但在数据帧上将被忽略。

The use of polling (3) has two premises: never use a blocking-mode of any .recv() -methods. 轮询(3)的使用有两个前提:切勿使用任何.recv()方法的阻塞模式。 ZeroMQ has flags to tell the method not to block: zmq.NOBLOCK on python side. ZeroMQ具有标志来告诉该方法不要阻塞:python端的zmq.NOBLOCK Plus, design the python code around a non-blocking form of .poll() or use a .Poller() -instance. 另外,围绕.poll()的非阻塞形式设计python代码,或使用.Poller()

Example: 例:

import zmq;                           print( zmq.zmq_version() ) # self-identify
aContext = zmq.Context();             print( "Context()", " instantiated." if zmq.zmq_errno() == 0 else " failed [#{}]".format( zmq.strerror( zmq.zmq_errno() ) ) )

aXmitSOCKET = aContext.socket( zmq.PUSH   ); aXmitSOCKET.setsockopt( zmq.LINGER, 0 ); ...
aCtrlSOCKET = aContext.socket( zmq.STREAM ); aCtrlSOCKET.setsockopt( zmq.LINGER, 0 ); ...

while True:
      if ( 0 == aXmitSOCKET.poll(  200, zmq.POLLIN ) ): # ~ 200 msec WAIT
         # ---------------------------------------------[aXmitPORT].hasNoIncomingMSG
         aCountDownREG -= 1                             #.DEC CDOWN as XmitPORT has no incoming DataToPREDICT_MSG
         aCountUpREG   += 1                             #.INC CNTUP
         if ( 0 == aCtrlSOCKET.poll( 1, zmq.POLLIN ) ): # ~   1 msec WAIT
            # ---------------------------------------------[aCtrlPORT].hasNoIncomingMSG
            ...
         else:                                          #
            # ---------------------------------------------[aCtrlPORT].hasAnIncomingMSG
            idF,aCMD = aCtrlSOCKET.recv_multipar( zmq.NOBLOCK )  # .recv()<-MSG as CtrlPORT has an incoming COMMAND_MSG
            ...
#--------------
# finally:
_ = [ aCtrlSOCKET.send_multipart( [ anIdentityFRAME, "" ], zmq.NOBLOCK ) for anIdentityFRAME in aListOfIdFRAMEs ]
aCtrlSOCKET.close()
aXmitSOCKET.close()
#--------------
# always:
aContext.term()

Feel free to also inspect the live-documentation of the methods: 还可以随时检查这些方法的实时文档:

>>> print( aCtrlSOCKET.recv_multipart.__doc__ )
Receive a multipart message as a list of bytes or Frame objects

        Parameters
        ----------
        flags : int, optional
            Any valid flags for :func:`Socket.recv`.
        copy : bool, optional
            Should the message frame(s) be received in a copying or non-copying manner?
            If False a Frame object is returned for each part, if True a copy of
            the bytes is made for each frame.
        track : bool, optional
            Should the message frame(s) be tracked for notification that ZMQ has
            finished with it? (ignored if copy=True)

        Returns
        -------
        msg_parts : list
            A list of frames in the multipart message; either Frames or bytes,
            depending on `copy`.

        Raises
        ------
        ZMQError
            for any of the reasons :func:`~Socket.recv` might fail

>>> print( aCtrlSOCKET.send_multipart.__doc__ )
Send a sequence of buffers as a multipart message.

        The zmq.SNDMORE flag is added to all msg parts before the last.

        Parameters
        ----------
        msg_parts : iterable
            A sequence of objects to send as a multipart message. Each element
            can be any sendable object (Frame, bytes, buffer-providers)
        flags : int, optional
            Any valid flags for :func:`Socket.send`.
            SNDMORE is added automatically for frames before the last.
        copy : bool, optional
            Should the frame(s) be sent in a copying or non-copying manner.
            If copy=False, frames smaller than self.copy_threshold bytes
            will be copied anyway.
        track : bool, optional
            Should the frame(s) be tracked for notification that ZMQ has
            finished with it (ignored if copy=True).

        Returns
        -------
        None : if copy or not track
        MessageTracker : if track and not copy
            a MessageTracker object, whose `pending` property will
            be True until the last send is completed.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM