SocketIO 从服务器发送消息时出现“数据包队列为空,正在中止”错误

问题描述 投票:0回答:4

我在从 WSGI 服务器向 SocketIO 客户端发送 SocketIO 消息时遇到问题。 另一种方式(客户端到服务器)的消息工作正常,没有任何问题。 该代码使用 Python 编写,使用

python-socketio
包和
eventlet
创建 WSGI 服务器。 服务器和客户端在同一台 Windows 计算机上的不同控制台中运行。 单击按钮即可发送消息。

发生的事情是这样的:

  1. 在一个控制台中,启动服务器并出现一个带有按钮的窗口 (发送消息)出现。
  2. 在另一个控制台中,启动客户端。 一扇窗户有两个 出现按钮 – 一键启动与服务器的连接 另一个按钮发送消息。
  3. 客户端的“Connect”按钮被点击,连接建立 没有问题。
  4. 客户端点击“发送消息”按钮,发送数据 在服务器的控制台中打印(这完全按照预期工作)
  5. 单击服务器的“发送消息”按钮。 在客户端控制台中, 有一段时间没有任何反应(我猜当服务器再次轮询时) 然后执行客户端的disconnect()函数(这不是 期望什么)

客户端控制台显示的消息略有不同,具体取决于服务器的客户端是否发送第一条消息。

  • 如果服务器发送第一条消息,那么当客户端发送 它只是来自disconnect()函数的打印语句的消息 显示。
  • 如果客户端发送第一条消息,那么当服务器发送消息时,“数据包队列 显示为空,正在中止”,然后是打印语句 connect() 函数。

为什么客户端收不到服务器发出的消息?

这是显示问题的完整服务器和客户端代码:

服务器代码:

from PyQt5.QtWidgets import QPushButton, QDialog, QApplication
import socketio, sys, eventlet
from threading import Thread

class My_Class(QDialog):

def __init__(self, parent=None):
    super(My_Class, self).__init__(parent)
    
    self.setWindowTitle("My SocketIO Server")
    self.resize(300,150)
    self.move(300, 200)
    
    self.btn1 = QPushButton(self)
    self.btn1.setText('Send Msg')
    self.btn1.move(100,75)
    self.btn1.clicked.connect(send_message_to_client)
    
    self.show()    

if __name__ == '__main__':

    sio = socketio.Server(async_mode='eventlet')

    def start_listening():
        eventlet.wsgi.server(eventlet.listen(('', 5000)), serverapp)
    
    @sio.event
    def connect(sid, environ):
        print('\nConnected with SID', sid)

    def send_message_to_client():
        print('\nSending from Button')
        sio.emit('Message_from_server', {"Message 1": "Hello"})

    @sio.event
    def message_from_client(sid, data):
        print('\nThis message came from the Client.', data, '\n')

    serverapp = socketio.WSGIApp(sio, static_files={'/': {'content_type': 'text/html', 'filename': 'index.html'}})
    thread = Thread(target = start_listening, args=())
    thread.start()

    app = QApplication(sys.argv)
    form = My_Class()
    form.show()
    sys.exit(app.exec_())

这是客户端代码:

from PyQt5.QtWidgets import QPushButton, QDialog, QApplication
import socketio, sys

class My_Client(QDialog):

def __init__(self, parent=None):
    super(My_Client, self).__init__(parent)
    
    self.setWindowTitle("My SocketIO Clent")
    self.resize(300,150)
    self.move(700, 200)
    
    self.btn1 = QPushButton(self)
    self.btn1.setText('connect')
    self.btn1.move(50,75)
    self.btn1.clicked.connect(connect)

    self.btn2 = QPushButton(self)
    self.btn2.setText('Send Msg')
    self.btn2.move(175,75)
    self.btn2.clicked.connect(send_message_from_client)
    
    self.show()    

if __name__ == '__main__':

    sio = socketio.Client()

    def connect():
        sio.connect('http://localhost:5000')
        print('\nConnection established using SID', sio.sid)

    @sio.event
    def message_from_server(sid, data):
        print('\nMessage from server received with ', data)
    
    def send_message_from_client():
        print('\nMessage sent from Client')
        sio.emit('message_from_client', {'Message': 'Hello World'})

    @sio.event
    def disconnect():
        print('\nDisconnected from server\n')

    app = QApplication(sys.argv)
    form = My_Client()
    form.show()
    sys.exit(app.exec_())
python server socket.io client python-socketio
4个回答
4
投票

我正在使用 socketio.asyncServer 并面临同样的问题。问题是我的服务器的 pingTimeout 太小(5 秒)。 pingTimeout 是客户端在断开连接之前等待服务器响应的时间(以秒为单位),默认设置为 5 秒。将超时间隔增加到 60 秒,解决了我的问题。 在服务器代码中,更改

sio = socketio.Server(async_mode='eventlet')

以下

sio = socketio.Server(async_mode='eventlet', ping_timeout=60)

sio = socketio.AsyncServer(ping_timeout=60)

1
投票

所以

packet queue is empty, aborting
是底层 python-engineio 包的产品,当发送空有效负载时触发,我相信当服务器端错过服务器和客户端之间正在进行的 PING / PONG 时。

因此,看起来您要处理的主要问题是客户端正在断开连接,因为服务器无法 PONG 返回客户端,并且客户端最终放弃并断开连接。

所以最大的问题是为什么服务器无法向客户端发送消息(但可以接收它们)。我猜想这与线程有关,要么通过在线程中运行 eventlet 服务器,要么将您的

sio
函数传递给 PyQt。我建议隔离独立于 PyQt 操作的连接,并尝试使用
multiprocessing
或直接运行服务器。


1
投票

从pythong-socketio问题来看,似乎你需要使用

sio.wait
。在#272#922

中提到过

您可以在

examples/client
示例中看到它们:

异步(

examples/client/async
)(链接):

async def start_server():
    await sio.connect('http://localhost:5000', auth={'token': 'my-token'})
    await sio.wait()


if __name__ == '__main__':
    asyncio.run(start_server())

主题(

examples/client/threads
)(链接):

if __name__ == '__main__':
    sio.connect('http://localhost:5000', auth={'token': 'my-token'})
    sio.wait()

所以,就你而言,它将是:

if __name__ == '__main__':

    sio = socketio.Client()

    def connect():
        sio.connect('http://localhost:5000')
        print('\nConnection established using SID', sio.sid)

        # === Add wait ===
        sio.wait()

我知道这是很久以前的问题,但这是我在谷歌上搜索错误消息时出现的唯一的帖子。

我花了很长时间才解决这个问题。我只是希望文档页面中提到了这一点。


0
投票

虽然 Nilesh Vijayrania 的解决方案(增加

pingTimeout
)是正确的,但他错误地解释了
pingTimeout
是什么。服务器具有“心跳”连接检测方法,其中服务器以某个预定义的时间间隔 (
pingInterval
) 发送 ping 数据包,客户端有几秒钟的时间 (
pingTimeout
) 来响应 pong 数据包。如果服务器在指定的 pingTimeout 内没有收到 pong 数据包,则认为连接已关闭并与客户端断开连接。

https://socket.io/docs/v4/how-it-works/ 请参阅“断线检测”部分

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.