简体   繁体   English

使 pynng 和 socket 相互通信

[英]Making pynng and socket talk to each other

TL;DR TL;博士

I spin up a server using pynng , then a client from Python Standard Library socket will try to send messages to it.我使用pynng启动服务器,然后来自 Python 标准库socket客户端将尝试向其发送消息。

The problem is that client can send the message, but server is oblivious to it.问题是客户端可以发送消息,但服务器没有注意到它。 Therefore, it doesn't work.因此,它不起作用。

Am I missing something?我错过了什么吗? Some low-level protocol setting?一些低级协议设置? Some termination character?一些终止字符?

The reason why I'm doing this is that I will build a Python script that uses pynng to act as a server.我这样做的原因是我将构建一个 Python 脚本,它使用pynng作为服务器。 Then a non-Python program (which I assume has knowledge of basic TCP protocols) will try to talk with this Python server.然后一个非 Python 程序(我假设它了解基本的 TCP 协议)将尝试与这个 Python 服务器通信。 Thus I am using the IMHO most primitive socket library I could operate, the socket module in the standard library.因此,我使用的是恕我直言,我可以操作的最原始的套接字库,标准库中的socket模块。

The details细节

I will present code snippets as I discuss, but I will show the full minimal code example at the end.我将在讨论时展示代码片段,但我将在最后展示完整的最小代码示例。

I am trying to spin up a server using pynng我正在尝试使用pynng启动服务器

def server():
    with pynng.Pair0(listen=f'tcp://{HOST:s}:{PORT:d}', recv_timeout=10000) as s:
        print("Server running")
        data = s.recv()  # Blocks forever here
        print(data)

Then, client that looks like this will try to connect to it:然后,看起来像这样的客户端将尝试连接到它:

def client():
    with socket.create_connection(address=(HOST, PORT), timeout=5) as s:
        print("Client connected")
        s.sendall(b'Hello world')
        print("Client sent message")

I put them all together in using threading :我将它们放在一起使用threading

def main():
    srv = threading.Thread(target=server)
    cli = threading.Thread(target=client)

    srv.start()
    cli.start()

    srv.join()
    cli.join()

Minimum working code最低工作代码

All told, this is the minimum working code:总而言之,这是最低限度的工作代码:

import socket
import pynng
import threading

HOST = "127.0.0.1"
PORT = 65432

def main():
    srv = threading.Thread(target=server)
    cli = threading.Thread(target=client)

    srv.start()
    cli.start()

    srv.join()
    cli.join()

def server():
    with pynng.Pair0(listen=f'tcp://{HOST:s}:{PORT:d}', recv_timeout=10000) as s:
        print("Server running")
        data = s.recv()  # Blocks forever here
        print("Message received")
        print(data)

def client():
    with socket.create_connection(address=(HOST, PORT), timeout=5) as s:
        print("Client connected")
        s.sendall(b'Hello world')
        print("Client sent message")


if __name__ == "__main__":
    main()

Then I run this in the terminal然后我在终端中运行它

$ python main.py

It seems that the server is unable to recv messages, and the recv attempt thus times out at 10000ms. server似乎无法recv消息,因此recv尝试在 10000 毫秒时超时。

Server running
Client connected
Client sent message
Exception in thread Thread-1:
Traceback (most recent call last):
  File "/home/kmonisit/miniconda3/envs/engg/lib/python3.8/threading.py", line 932, in _bootstrap_inner
    self.run()
  File "/home/kmonisit/miniconda3/envs/engg/lib/python3.8/threading.py", line 870, in run
    self._target(*self._args, **self._kwargs)
  File "main.py", line 39, in server
    data = s.recv()  # Blocks forever here
  File "/home/kmonisit/miniconda3/envs/engg/lib/python3.8/site-packages/pynng/nng.py", line 454, in recv
    check_err(ret)
  File "/home/kmonisit/miniconda3/envs/engg/lib/python3.8/site-packages/pynng/exceptions.py", line 201, in check_err
    raise exc(string, err)
pynng.exceptions.Timeout: Timed out

pynng is based on Nanomsg Next Generation , which is an implementation of the Scalability Protocols . pynng基于Nanomsg Next Generation ,它是Scalability Protocols的一种实现。 The scalability protocols work on many different transports, including tcp, but bare sockets are not compatible.可扩展性协议适用于许多不同的传输,包括 tcp,但sockets不兼容。 However, with a little bit of prayer and elbow grease, they can be made compatible.但是,只要稍加祈祷和肘部油脂,它们就可以兼容。 Which is too say, you can implement the scalability protocols in pure Python if need be.也就是说,如果需要,您可以在纯 Python 中实现可扩展性协议。

First, we need to know what the wire format is;首先,我们需要知道有线格式是什么; thankfully that is documented in an RFC in the original nanomsg repository .谢天谢地,这在原始 nanomsg 存储库的 RFC 中有记录 An implementation of a Pair0 client is here: Pair0客户端的实现在这里:

class Pair0:
    """A poor implementation of the Pair0 protocol"""

    def __init__(self, host, port, timeout=None):
        self._sock = socket.create_connection(address=(host, port), timeout=timeout)
        # https://github.com/nanomsg/nanomsg/blob/master/rfc/sp-tcp-mapping-01.txt
        # upon making a connection, both ends are required to send this header
        self._sock.send(b'\x00SP\x00\x00\x10\x00\x00')
        print(self._sock.recv(8))

    def send(self, data):
        # messages are simply "length + payload".  Length is 64-bit in network byte
        # order.
        packed = struct.pack('!Q', len(data))
        self._sock.sendall(packed + data)

    def recv(self):
        size_bytes = self._sock.recv(8)
        (size,) = struct.unpack('!Q', size_bytes)
        received = 0
        parts = []
        while received < size:
            data = self._sock.recv(size - received)
            received += len(data)
            parts.append(data)
        return b''.join(parts)

And integrated into your test program:并集成到您的测试程序中:

import socket
import struct
import pynng
import threading
import time

HOST = "127.0.0.1"
PORT = 65432


def main():
    srv = threading.Thread(target=server)

    srv.start()
    # sleep to give the server time to bind to the address
    time.sleep(0.1)
    _client = Pair0(HOST, PORT, 1)
    _client.send(b'hello pynng')
    _client.send(b'hope everything is going well for you')
    print(_client.recv())
    print(_client.recv())
    srv.join()


def server():
    with pynng.Pair0(listen=f'tcp://{HOST:s}:{PORT:d}', recv_timeout=1000) as s:
        print("Server running")
        for _ in range(2):
            data = s.recv()
            print("Message received")
            print(data)
        s.send(b'hello bad client')
        s.send(b'I hope you are doing okay')


class Pair0:
    """A poor implementation of the Pair0 protocol"""

    def __init__(self, host, port, timeout=None):
        self._sock = socket.create_connection(address=(host, port), timeout=timeout)
        # https://github.com/nanomsg/nanomsg/blob/master/rfc/sp-tcp-mapping-01.txt
        # upon making a connection, both ends are required to send this header
        self._sock.send(b'\x00SP\x00\x00\x10\x00\x00')
        print(self._sock.recv(8))

    def send(self, data):
        # messages are simply "length + payload".  Length is 64-bit in network byte
        # order.
        packed = struct.pack('!Q', len(data))
        self._sock.sendall(packed + data)

    def recv(self):
        size_bytes = self._sock.recv(8)
        (size,) = struct.unpack('!Q', size_bytes)
        received = 0
        parts = []
        while received < size:
            data = self._sock.recv(size - received)
            received += len(data)
            parts.append(data)
        return b''.join(parts)


if __name__ == "__main__":
    main()

Now, this is nowhere near as robust as the implementation in pynng (which relies on the underlying nng implementation).现在,这远不及 pynng 中的实现(依赖于底层的 nng 实现)那么健壮。 nng does The Right Thing™ in edge conditions, including losing network, handling multiple clients, keeping track of state machines, handling SIGINT, etc. This is also an imcomplete implementation, as it does not bind , etc. nng 在边缘条件下执行 The Right Thing™,包括丢失网络、处理多个客户端、跟踪 state 机器、处理 SIGINT 等。这也是一个不完整的实现,因为它不bind等。

Disclaimer: I am the author of pynng.免责声明:我是pynng的作者。

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

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