[英]Making pynng and socket talk to each other
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
模块。
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()
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.