如何在不停止反应器的情况下停止 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 客户端的主要内容,如果未能解决你的问题,请参考以下文章

在薄轨上安全地停止 em-websocket

如何在 Ubuntu 上停止 Node.js 并在不停止的情况下注销 [重复]

如何在不停止应用程序的情况下延迟功能

如何在不停止站点的情况下更新 ASP.Net 站点 dll

如何在不停止的情况下将物体朝一个方向移动

如何在不停止 laravel 程序的情况下使用 dd()