很抱歉,很长的帖子,但是我已经为此花了一个多星期,所以我尝试了很多不同的东西。我对Python非常了解,但是我对Python的异步或非阻塞函数没有任何经验。
我正在为需要Websocket连接的Web服务编写API库/模块/程序包/其他内容。有许多要处理的传入消息,并且有时需要发送一些与控制相关的(Web应用程序级别,而不是Websocket控制消息)。我可以轻松地通过连接接收消息并对其执行操作。我可以发送消息,但是只能响应收到的消息,因为接收循环始终阻止等待消息。我不想等待传入的消息处理传出的消息,因此该脚本不必在输入新消息之前就挂在输入上。在努力使双向通信正常工作的过程中,我发现我需要使用Twisted,Tornado或asyncio之类的东西,但到目前为止,我尝试过的每个实现都失败了。请注意,发送必须在同一连接上进行。在接收循环之外打开一个短暂的连接将不起作用。到目前为止,这是我所做的:
websocket代码的第一次迭代是使用websocket-client包。这与文档中的示例非常接近:
import websocket
try:
import thread
except ImportError:
import _thread as thread
import time
def on_message(ws, message):
# Send message frames to respective functions
# for sorting, objectification, and processing
def on_error(ws, error):
print(error)
def on_close(ws):
print("### closed ###")
def on_open(ws):
def run(*args):
# Send initial frames required for server to send the desired frames
thread.start_new_thread(run, ())
if __name__ == "__main__":
websocket.enableTrace(True)
ws = websocket.WebSocketApp(buildWebsocketURL()),
on_message = on_message,
on_error = on_error,
on_close = on_close)
ws.on_open = on_open
ws.run_forever()
这将阻止循环外的任何进一步执行。我尝试学习_thread模块,但找不到任何迹象表明我可以从外部与websocket线程“通信”。我尝试设置一个pub / sub侦听器函数,该函数会将数据从另一个发送器函数转发到ws.send()
,但没有用。没有错误或任何东西,只是没有任何已发送消息的指示。
接下来,我尝试了Websockets模块。这似乎是从头开始构建的,以利用异步。再次,我得到了一个客户端构建,该客户端构建将发送初始消息并对接收到的消息进行操作,但是进度在那里停止:
async def wsconnection():
async with websockets.connect(getWebsocketURL()) as websocket:
while True:
message = await websocket.recv()
if message == '{"type":"broadcaster.ready"}':
subscriptions = getSubscriptions() # Get subscriptions from ident data
logging.info('Sending bookmarks to server as subscription keys')
subscriptionupdate = '{{"type": "subscribe","subscription_keys": ["{0}"],"subscription_scope": "update"}}'.format(
'","'.join(subscriptions))
subscriptioncontent = '{{"subscription_keys": ["{0}"],"subscription_scope": "content","type": "subscribe"}}'.format(
'","'.join(subscriptions))
logging.debug(subscriptioncontent)
await websocket.send(subscriptionupdate)
await websocket.send(subscriptioncontent)
await websocket.send(
'{"type":"message_lobby.read","lobby_id":"1","message_id:"16256829"}')
sortframe(message)
asyncio.get_event_loop().run_until_complete(wsconnection())
我尝试了上述发布/订阅侦听器在这里的应用,但没有成功。在更彻底地阅读了该模块的文档之后,我尝试将websocket协议对象(包含send()和recv()方法)移出循环,然后创建两个协程(?),一个侦听传入的消息,另一个侦听并发送外发消息。到目前为止,如果在wsconnection()函数范围内不运行async with websockets.connect(getWebsocketURL()) as websocket:
行,我将完全无法获取websocket协议对象。我尝试使用websocket = websockets.client.connect()
,根据我认为的docs,它可以设置我需要的协议对象,但不需要。我能找到的所有示例似乎都没有揭示任何明显的方式来构造Websocket发送方和接收方,而无需广泛了解asyncio。
我也使用asyncio和Twisted并使用了与以上类似的代码结构的autobahn,但我遇到了与上面相同的所有问题。
到目前为止,我最接近的是上面的Websockets软件包。这些文档有一个用于发送/接收连接的example snippet,但是我真的看不懂发生了什么,因为它们都是非常特定于asyncio的。总的来说,我真的很难绕过asyncio,我认为一个大问题是,它最近似乎发展非常迅速,因此围绕该冲突有大量非常特定于版本的信息。不幸的是,这不利于学习。 ~~~~这是我在该示例中尝试过的方法,它可以连接,接收初始消息,然后连接丢失/关闭:
async def producer(message):
print('Sending message')
async def consumer_handler(websocket, path):
while True:
message = await websocket.recv()
await print(message)
await pub.sendMessage('sender', message)
async def producer_handler(websocket, path):
while True:
message = await producer()
await websocket.send(message)
async def wsconnect():
async with websockets.connect(getWebsocketURL()) as websocket:
path = "443"
async def handler(websocket, path):
consumer_task = asyncio.ensure_future(
consumer_handler(websocket, path))
producer_task = asyncio.ensure_future(
producer_handler(websocket, path))
done, pending = await asyncio.wait(
[consumer_task, producer_task],
return_when=asyncio.FIRST_COMPLETED,
)
for task in pending:
task.cancel()
pub.subscribe(producer, 'sender')
asyncio.get_event_loop().run_until_complete(wsconnect())
那么我该如何构造此代码以通过同一websocket连接进行发送和接收?在打开websocket连接的同时,我还需要在同一脚本中进行各种API调用,这使事情变得更加复杂。
我正在使用Python 3.6.6,并且该脚本打算作为模块导入其他脚本中,因此需要将websocket功能包装在用于外部调用的函数或类中。
答案 0 :(得分:0)
我和你的处境完全相同。我知道这是一个非常微妙的解决方案 因为它仍然不是全双工的,但是我似乎在互联网或stackoverflow上找不到涉及asyncio和我使用的websockets模块的任何示例。
我认为我不完全理解您的websockets示例(是服务器端还是客户端代码?),但我将解释我的情况和“解决方案”,也许对您也有用。
因此,我有一个服务器主要功能,该功能具有一个websocket,可使用recv()循环监听消息。当我发送“开始”消息时,它将启动一个函数,该函数每秒将数据发送到浏览器中的javascript客户端。但是,当函数正在发送数据时,有时我想暂停或停止来自客户端的数据流,并发送停止消息。问题是当数据发送已经开始时我使用recv()时,服务器停止发送数据,仅等待消息。我尝试了线程,多处理和其他一些东西,但是最终我想到了一个临时的解决方案,希望在客户端收到一段数据后立即向服务器发送“ pong”消息,以便服务器在下一次循环迭代或例如,如果“ pong”消息为“ stop”,则停止发送数据,但是是的,这不是真正的双工,而是快速的半双工...
我的python“服务器”上的代码
async def start_server(self,websocket,webserver_path):
self.websocket = websocket
self.webserver_path = webserver_path
while True:
command = await self.websocket.recv()
print("received command")
if command == "start":
await self.analyze()
asyncio.sleep(1)
在我的分析功能中:
for i,row in enumerate(data)
await self.websocket.send(json.dumps(row))
msg = await self.websocket.recv()
if msg == "stop":
self.stopFlag = True
return
await asyncio.sleep(1)
主要
start_server = websockets.serve(t.start_server, "127.0.0.1", 5678)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
JavaScript客户端上的代码
var ws = new WebSocket("ws://127.0.0.1:5678/");
ws.onmessage = function (event) {
var datapoint = JSON.parse(event.data);
console.log(counter);
counter++;
data.push(datapoint);
if (data.length > 40){
var element = data.shift();
render(data);
}
ws.send("pong");//sending dummy message to let server continue
};
我知道这不是解决方案,我希望其他人可以提供更好的解决方案,但是由于我有相同或非常相似的问题,并且没有其他答案,因此我决定发布,希望对您有所帮助。