Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/18.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python异步IO队列get不';我没有收到消息_Python_Python 3.x_Asynchronous_Queue_Python Asyncio - Fatal编程技术网

Python异步IO队列get不';我没有收到消息

Python异步IO队列get不';我没有收到消息,python,python-3.x,asynchronous,queue,python-asyncio,Python,Python 3.x,Asynchronous,Queue,Python Asyncio,我发布了一个与get from队列相关的新问题。 这是代码(感谢Martijn Pieters) 此代码是由从父进程调用的子进程 subprocess.Popen(["python3", test], stdin=subprocess.PIPE, stdout=subprocess.PIPE, bufsize=2048) 问题是socket\u consumer(从套接字接收)将对象放入队列中,但pipe\u producer不会从incoming.get()开始。 文件写入仅用于测试目的 目

我发布了一个与get from队列相关的新问题。 这是代码(感谢Martijn Pieters)

此代码是由从父进程调用的子进程

subprocess.Popen(["python3", test], stdin=subprocess.PIPE, stdout=subprocess.PIPE, bufsize=2048)
问题是
socket\u consumer
(从套接字接收)将对象放入队列中,但
pipe\u producer
不会从
incoming.get()
开始。 文件写入仅用于测试目的

目前的家长是这样的(仅用于测试)

相反,要发送到web套接字,我使用以下代码:

#!/usr/bin/env python

import asyncio
import websockets

async def hello(uri):
    header = {"Authorization": r"Basic XXXX="}
    message = '{"header":{"protocolVersion":1,"messageID":2,"stationID":400},"cam":{"generationDeltaTime":1,"camParameters":{"basicContainer":{"stationType":5,"referencePosition":{"latitude":451114425,"longitude":76720957,"positionConfidenceEllipse":{"semiMajorConfidence":4095,"semiMinorConfidence":4095,"semiMajorOrientation":3601},...other fields}}';
    async with websockets.connect(uri, extra_headers=header) as websocket:
        await websocket.send(message)


asyncio.get_event_loop().run_until_complete(
    hello('XXX'))
它通过管道发送并工作,因为我通过管道接收并发送到套接字(pipe.txt和ToSocket.txt中的文件是正确的)。
然后,我将代码发送到具有打开的web套接字的服务器,该服务器将消息发送给子服务器。当孩子从套接字接收文件时,会创建FromSocket.txt文件,但直到我将ToPipe.txt文件放在
awit incoming.get()之前,才会创建ToPipe.txt文件

FromSocket.txt
包含以下内容:

From socket: '{"header":{"protocolVersion":1,"messageID":2,"stationID":400},"cam":{"generationDeltaTime":1, ... other field}}'
但是,如果类型检索出现问题,它将创建文件,因为它是
json\u message=wait incoming.get()之后的第一条指令。
我认为这是排队的问题。 对于测试,我在wait
outing.put(message)
之后,将
incoming.get()
放入socket\u消费者中,它就工作了

更新:如果我只运行子级(因此没有管道),ToPipe.txt是正确的,消息transfert from socket to pipe是正确的。
对于我的测试,我运行父级,它向管道发送一条消息,子级发送到套接字,然后我向套接字发送一条消息,子级捕获该消息,但它不发送到管道,并且不会创建ToPipe.txt。可能主方法中有问题

您正在向子进程写入双编码JSON:

message = '{"header":{"protocolVersion":1,"messageID":2,"stationID":400}, the rest of json...}}';
jsonValue = json.dumps(message)
message
已经是一个JSON字符串,因此
jsonValue
是一个双重编码的JSON字符串

管道使用者将此双重编码字符串推送到套接字队列中。接下来,
socket\u producer()
中的websocket producer再次对消息进行编码:

while True:
    message = await incoming.get()
    # ...
    json_message = json.dumps(message)
    await socket.send(json_message)
所以现在
json\u message
是一个三重编码的json值,一个json文档包含一个json文档,其中包含一个json文档:

>>> import json
>>> message = '{"header":{"protocolVersion":1,"messageID":2,"stationID":400}}}'  # valid JSON
>>> json_message = json.dumps(message)
>>> print(json_message)  # double-encoded
"{\"header\":{\"protocolVersion\":1,\"messageID\":2,\"stationID\":400}}}"
>>> json_message = json.dumps(json_message)  # encode *again*
>>> print(json_message)  # triple-encoded
"\"{\\\"header\\\":{\\\"protocolVersion\\\":1,\\\"messageID\\\":2,\\\"stationID\\\":400}}}\""
message = json.loads(json_message)
type = int(message.get('header', {}).get('messageID', -1))
我不知道您的web套接字对此做了什么,但是让我们假设它使用
json.loads()
一次,然后回显解码后的消息。这意味着,
socket\u consumer()
只接收两次编码的JSON文档。您的
FromSocket.txt
日志肯定意味着这就是发生的情况,因为它包含一条双重编码的JSON消息:

您可以在
FromSocket.txt
日志中看到这一点:

From socket: "{\"header\":{\"protocolVersion\":1,\"messageID\":2,\"stationID\":400},\"cam\":{\"generationDeltaTime\":1,...other fields}}"
请注意那些
\”
条目,整个文档都用引号括起来,但值中没有三个反斜杠

尽管如此,JSON编码的这种额外分层破坏了
管道\u producer()
协同程序,它期望消息解码到字典,而不是另一个字符串(即使该字符串包含另一个JSON文档):

message
将改为解码为字符串,因此
message.get
将失败,出现
AttributeError
,导致协同程序退出:

>>> json_message = "{\"header\":{\"protocolVersion\":1,\"messageID\":2,\"stationID\":400}}}"  # double encoded
>>> message = json.loads(json_message)
>>> message  # Back one stop, single-encoded JSON
'{"header":{"protocolVersion":1,"messageID":2,"stationID":400}}}'
>>> type(message)  # it's a string with JSON, not a dictionary
<class 'str'>
>>> message.get('header')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'get'
您可能希望记录解码在某个地方失败(将消息推送到一个日志队列,由一个单独的任务选择写入日志)

接下来,我们可以更新
connect.*
函数,以不忽略完成的任务中的异常:

done, pending = await asyncio.wait(
    [consumer_task, producer_task], return_when=asyncio.FIRST_COMPLETED)
for task in pending:
    task.cancel()
# force a result check; if there was an exception it'll be re-raised
for task in done:
    task.result()
done.result()
检查可以重新引发消费者或生产者中抛出的异常。由于
connect.*
协同路由是通过
asyncio.gather()
运行的,而异步io.gather()
反过来又由
循环运行。运行直到\u complete()
,因此该异常会一直传播到
main()
函数,因此它将退出Python,您可以看到打印的回溯。我更新了另一个答案,将任务的
包含在done:task.result()中,因为这是一个很好的实践

只需在我的原始答案代码中使用
task.result()
循环,以及一个只回显消息的websocket,并输入一个有效的JSON文档(不是双重编码),我可以立即看到错误;这里的父进程是我的终端,所以我只是将JSON消息复制到我的终端窗口中,以便将数据发送到管道中:

$ python3.7 so52291218.py
{"header":{"protocolVersion":1,"messageID":2,"stationID":400}}
Traceback (most recent call last):
  File "so52291218.py", line 140, in <module>
    main()
  File "so52291218.py", line 137, in main
    loop.run_until_complete(asyncio.gather(socket_coro, pipe_coro))
  File "/.../lib/python3.7/asyncio/base_events.py", line 568, in run_until_complete
    return future.result()
  File "so52291218.py", line 126, in connect_pipe
    task.result()
  File "so52291218.py", line 104, in pipe_producer
    type = int(message.get("header", {}).get("messageID", -1))
AttributeError: 'str' object has no attribute 'get'
因此,每次我们向管道发送一条完整的线路时,我们也会在短时间内寻找另一条线路,并回送任何此类线路

所有修复都已就绪(确保避免多次编码JSON消息),并且非常简单,上面的
pexpect
代码将打印:

>>> {"header":{"protocolVersion":1,"messageID":2,"stationID":400}}
>>> {"header":{"protocolVersion":1,"messageID":2,"stationID":400}}
>>> {"header":{"protocolVersion":1,"messageID":2,"stationID":400}}
>>> {"header":{"protocolVersion":1,"messageID":2,"stationID":400}}
>>> {"header":{"protocolVersion":1,"messageID":2,"stationID":400}}

显示从父进程到子进程到websocket再到websocket有一个完整的往返路径。

您是如何从父进程Popen读写的?您使用的是
select
还是多线程?您可以尝试使用吗?这样可以更容易地确保iss不是父进程下一步,我们需要更多关于此处交换的实际消息的详细信息,以确保
pipe\u producer
解析消息的方式没有问题。能否显示
ToPipe.txt
输出以及父进程和websocket正在发送和接收的数据?我添加了更多详细信息您从未从管道stdin读取数据。这可能会导致整个I/O层完全暂停,并且您的写入操作也不会进入子进程。请务必在此处使用
pexpect
,以避免类似的问题。我在下面的回答中概述了还有哪些问题。问题在于,您从未指定管道和WebSock的数据类型t发送和接收;我认为您已经解决了这些部分。感谢您的解释,我删除了json编码
done, pending = await asyncio.wait(
    [consumer_task, producer_task], return_when=asyncio.FIRST_COMPLETED)
for task in pending:
    task.cancel()
# force a result check; if there was an exception it'll be re-raised
for task in done:
    task.result()
$ python3.7 so52291218.py
{"header":{"protocolVersion":1,"messageID":2,"stationID":400}}
Traceback (most recent call last):
  File "so52291218.py", line 140, in <module>
    main()
  File "so52291218.py", line 137, in main
    loop.run_until_complete(asyncio.gather(socket_coro, pipe_coro))
  File "/.../lib/python3.7/asyncio/base_events.py", line 568, in run_until_complete
    return future.result()
  File "so52291218.py", line 126, in connect_pipe
    task.result()
  File "so52291218.py", line 104, in pipe_producer
    type = int(message.get("header", {}).get("messageID", -1))
AttributeError: 'str' object has no attribute 'get'
import sys
import pexpect

test = '...'
process = pexpect.popen_spawn.PopenSpawn([sys.executable, test])

for i in range(5):
    message = '{"header":{"protocolVersion":1,"messageID":2,"stationID":400}}';
    jsonValueBytes = message.encode("utf-8")
    process.send(jsonValueBytes + b"\n")

    # echo anything coming back
    while True:
        index = process.expect([process.crlf, pexpect.EOF, pexpect.TIMEOUT], timeout=0.1)
        if not process.before:
            break
        print('>>>', process.before.decode('utf8', errors='replace'), flush=True)

# send EOF to close the pipe, then terminate the process
process.sendeof()
process.kill(1)
process.wait()
>>> {"header":{"protocolVersion":1,"messageID":2,"stationID":400}}
>>> {"header":{"protocolVersion":1,"messageID":2,"stationID":400}}
>>> {"header":{"protocolVersion":1,"messageID":2,"stationID":400}}
>>> {"header":{"protocolVersion":1,"messageID":2,"stationID":400}}
>>> {"header":{"protocolVersion":1,"messageID":2,"stationID":400}}