Python中简单的ZMQ客户端/服务器发送多行请求
这是我第一次在Python中接触ZMQ,我希望服务器在收到客户端的请求时能够发送多行数据。我在ZMQ提供的示例代码中,添加到服务器端的代码如下:
with open("test.txt", 'r') as f:
for line in f:
socket.send_string(line.rstrip("\n"))
我的问题是,如何让服务器发送所有行,或者如何让客户端在服务器发送完test.txt
中的所有行之前,不发送请求。
客户端
import zmq
context = zmq.Context()
print("Connecting to hello world server")
socket = context.socket(zmq.REQ)
socket.connect("tcp://localhost:5555")
for request in range(10):
print("Sending request %s" % request)
socket.send(b"Hello")
message = socket.recv()
print("Received reply %s [ %s ]" % (request, message))
服务器
import time
import zmq
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind("tcp://*:5555")
while True:
# Wait for next request from client
message = socket.recv()
print("Received request: %s" % message)
# Do some 'work'
time.sleep(1)
# Send reply back to client
with open("test.txt", 'r') as f:
for line in f:
socket.send_string(line.rstrip("\n"))
客户端日志
Connecting to hello wolrd server
Sending request 0
Received reply 0 [ This is test line 1 ]
Sending request 1
这里是停止的地方,因为服务器生成了下面显示的错误:
服务器日志
line 324, in send_string
return self.send(u.encode(encoding), flags=flags, copy=copy)
File "socket.pyx", line 571, in zmq.backend.cython.socket.Socket.send (zmq/backend/cython/socket.c:5319)
File "socket.pyx", line 618, in zmq.backend.cython.socket.Socket.send (zmq/backend/cython/socket.c:5086)
File "socket.pyx", line 181, in zmq.backend.cython.socket._send_copy (zmq/backend/cython/socket.c:2081)
File "checkrc.pxd", line 21, in zmq.backend.cython.checkrc._check_rc (zmq/backend/cython/socket.c:6032)
zmq.error.ZMQError: Operation cannot be accomplished in current state
Process finished with exit code 1
test.txt
This is test line 1
This is test line 2
This is test line 3
This is test line 4
This is test line 5
2 个回答
6
这是我想到的解决方案……以防将来有谁需要帮助解决类似的问题。这个想法是把所有的行打包成一条消息,然后发送回客户端。看起来每次客户端发请求时,服务器都需要回复一次,而且只能回复一次。至少我觉得是这样的……
在服务器端替换代码
# Send reply back to client
with open("test.txt", 'r') as f:
for line in f:
socket.send_string(line.rstrip("\n"))
为:
# Send reply back to client
with open("test.txt", 'r') as f:
message = '%s' % f.readlines()
print(message)
print(type(message))
socket.send_string(message)
客户端请求
Connecting to hello world server
Sending request 0
Received reply 0 [ ['This is test line 1\n', 'This is test line 2\n', 'This is test line 3\n', 'This is test line 4\n', 'This is test line 5\n', 'This is test line 6\n', 'This is test line 7\n', 'This is test line 8\n', 'This is test line 9\n', 'This is test line 10\n', '\n'] ]
Sending request 1
Received reply 1 [ ['This is test line 1\n', 'This is test line 2\n', 'This is test line 3\n', 'This is test line 4\n', 'This is test line 5\n', 'This is test line 6\n', 'This is test line 7\n', 'This is test line 8\n', 'This is test line 9\n', 'This is test line 10\n', '\n'] ]
....
....
and so on up to 10 requests
服务器响应
Received request: Hello
['This is test line 1\n', 'This is test line 2\n', 'This is test line 3\n', 'This is test line 4\n', 'This is test line 5\n', 'This is test line 6\n', 'This is test line 7\n', 'This is test line 8\n', 'This is test line 9\n', 'This is test line 10\n', '\n']
<type 'str'>
Received request: Hello
['This is test line 1\n', 'This is test line 2\n', 'This is test line 3\n', 'This is test line 4\n', 'This is test line 5\n', 'This is test line 6\n', 'This is test line 7\n', 'This is test line 8\n', 'This is test line 9\n', 'This is test line 10\n', '\n']
<type 'str'>
....
....
....
and so on....
现在这个问题解决了,接下来的问题是:客户端需要发送什么样的请求,才能接受到服务器逐行的响应。如果我有解决方案会更新这个回复,或者你也可以随时分享你的想法。
3
好吧,你提到的解决方案正是我喜欢的,那就是把所有内容作为一条消息发送,如果需要的话可以使用不同的框架。不过,之所以只能发送一条回复,是因为你使用的是REQ-REP
这种套接字对。在这种情况下,你必须遵循一个简单的“请求-回复-请求-回复”的模式。每次交流都必须以一个请求开始,接下来的消息必须是一个回复,然后再是请求,依此类推。
为了绕过这个限制,你有几个选择:
- 就像你做的那样,把你想发送的所有内容打包成一条消息。在你的情况下,只要没有什么限制,这将是首选方案,因为所有数据实际上都是来自一个逻辑文件的一个逻辑消息,接收端也会把它们组合成一个逻辑数据块,我想是这样的。
- 如果你想继续使用
REQ-REP
套接字对,那么你可以先发送一个请求,然后是一个回复,接下来的请求可以简单地是“更多”之类的,接着发送下一部分数据,继续请求“更多”,直到你的客户端回复“完成”,这时你就知道所有数据都收到了,可以停止请求更多。 - 或者,如果你希望能够对单个请求进行多次回复,或者在收到回复之前多次请求,那么你需要使用
ROUTER-DEALER
套接字对,而不是REQ-REP
。在这种情况下,你的DEALER
套接字将替代REQ
套接字,而ROUTER
将替代REP
。查看手册,如果在实现ROUTER-DEALER
时遇到困难,可以再问一个新问题。