如何在不停止反应器的情况下停止 websocket 客户端
Posted
技术标签:
【中文标题】如何在不停止反应器的情况下停止 websocket 客户端【英文标题】:How to stop a websocket client without stopping reactor 【发布时间】:2017-09-16 10:36:24 【问题描述】:我有一个类似于聊天室的应用程序,用 python 编写,打算做以下事情:
-
提示用户输入 websocket 服务器地址。
然后创建一个连接到服务器并发送/接收消息的 websocket 客户端。禁用创建 websocket 客户端的功能。
从服务器接收到“关闭”(不是关闭帧)后,客户端应断开连接并重新启用应用程序以创建客户端。回到 1。
如果用户退出应用程序,如果有一个正在运行的 websocket 客户端,它就会退出。
我的方法是使用主线程来处理用户输入。当用户按下回车键时,使用 AutoBahn 的 twisted 模块为 WebSocketClient 创建一个线程并将队列传递给它。检查反应堆是否正在运行,如果没有则启动它。 覆盖消息方法以在“关闭”时将关闭标志放入队列。主线程将忙于检查队列,直到收到标志并返回开始。代码如下所示。
主线程。
def main_thread():
while True:
text = raw_input("Input server url or exit")
if text == "exit":
if myreactor:
myreactor.stop()
break
msgq = Queue.Queue()
threading.Thread(target=wsthread, args=(text, msgq)).start()
is_close = False
while True:
if msgq.empty() is False:
msg = msgq.get()
if msg == "close":
is_close = True
else:
print msg
if is_close:
break
print 'Websocket client closed!'
工厂和协议。
class MyProtocol(WebSocketClientProtocol):
def onMessage(self, payload, isBinary):
msg = payload.decode('utf-8')
self.Factory.q.put(msg)
if msg == 'close':
self.dropConnection(abort=True)
class WebSocketClientFactoryWithQ(WebSocketClientFactory):
def __init__(self, *args, **kwargs):
self.queue = kwargs.pop('queue', None)
WebSocketClientFactory.__init__(self, *args, **kwargs)
客户端线程。
def wsthread(url, q):
factory = WebSocketClientFactoryWithQ(url=url, queue=q)
factory.protocol = MyProtocol
connectWS(Factory)
if myreactor is None:
myreactor = reactor
reactor.run()
print 'Done'
现在我遇到了问题。看来我的客户线程永远不会停止。即使我收到“关闭”,它似乎仍在运行,每次我尝试重新创建新客户端时,它都会创建一个新线程。我知道第一个线程不会停止,因为reactor.run()
将永远运行,但是从第二个线程开始,它应该是非阻塞的,因为我不再启动它了。我该如何改变呢?
编辑:
我最终解决了它
-
断开连接后添加
stopFactory()
。
用reactor.callFromThread()
制作协议函数。
在第一个线程中启动反应器,将客户端放在其他线程中,并使用reactor.callInThread()
创建它们。
【问题讨论】:
服务器可以关闭客户端套接字,你想处理服务器上的所有客户端错误吗? @dsgdfg 事实上,我正在尝试复制现有应用程序,但我不拥有客户端或服务器,因此我没有代码。根据我对使用wireshark的了解,我可以看到服务器发送消息“关闭”并且客户端使用代码1001回复关闭帧并退出。我也想这样做,但我不知道如何关闭客户端并让线程返回以便我可以做其他工作。 【参考方案1】:您的main_thread
创建运行wsthread
的新线程。 wsthread
使用 Twisted API。 first wsthread
成为反应器线程。所有后续线程都是不同的,如果您使用来自它们的 Twisted API 会发生什么是未定义的。
您几乎肯定应该从您的应用程序中删除线程的使用。要在基于 Twisted 的应用程序中处理控制台输入,请查看 twisted.conch.stdio
(唉,这不是 Twisted 中记录最好的部分,但正是您想要的)。
【讨论】:
感谢您指出这一点!我认为这可能是问题所在。我知道使用单线程可能更适合 Twisted,但我需要做一些事情 -> 启动 websocket 客户端 -> 根据结果做一些事情,重复。据我所知,我需要做 reactor.run() 来启动 websocket 客户端,但它会永远阻塞。有没有更好的方法在不使用线程的情况下完成上述操作?我还有一个 GUI 线程,我不知道不使用多线程是否好。 Twisted 有各种 GUI 集成。根据哪个工具包,您可能能够找到一种在单个线程中运行 GUI 和 Twisted 的方法。此外,如果您熟悉 GUI 编程(无论如何都没有额外的线程),那么很多这些想法也适用于 Twisted。例如,您不会在 Tkinter 中编写while True:
循环......您使用的是 after_idle
API。同样,Twisted 有 API 允许您安排更多代码稍后运行,以避免出现阻塞反应器线程的循环(并避免您被reactor.run
阻塞的事实阻塞)。以上是关于如何在不停止反应器的情况下停止 websocket 客户端的主要内容,如果未能解决你的问题,请参考以下文章