我在从 WSGI 服务器向 SocketIO 客户端发送 SocketIO 消息时遇到问题。 另一种方式(客户端到服务器)的消息工作正常,没有任何问题。 该代码使用 Python 编写,使用
python-socketio
包和 eventlet
创建 WSGI 服务器。 服务器和客户端在同一台 Windows 计算机上的不同控制台中运行。 单击按钮即可发送消息。
发生的事情是这样的:
客户端控制台显示的消息略有不同,具体取决于服务器的客户端是否发送第一条消息。
为什么客户端收不到服务器发出的消息?
这是显示问题的完整服务器和客户端代码:
服务器代码:
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_())
我正在使用 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)
所以
packet queue is empty, aborting
是底层 python-engineio 包的产品,当发送空有效负载时触发,我相信当服务器端错过服务器和客户端之间正在进行的 PING / PONG 时。
因此,看起来您要处理的主要问题是客户端正在断开连接,因为服务器无法 PONG 返回客户端,并且客户端最终放弃并断开连接。
所以最大的问题是为什么服务器无法向客户端发送消息(但可以接收它们)。我猜想这与线程有关,要么通过在线程中运行 eventlet 服务器,要么将您的
sio
函数传递给 PyQt。我建议隔离独立于 PyQt 操作的连接,并尝试使用 multiprocessing
或直接运行服务器。
从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()
我知道这是很久以前的问题,但这是我在谷歌上搜索错误消息时出现的唯一的帖子。
我花了很长时间才解决这个问题。我只是希望文档页面中提到了这一点。
虽然 Nilesh Vijayrania 的解决方案(增加
pingTimeout
)是正确的,但他错误地解释了 pingTimeout
是什么。服务器具有“心跳”连接检测方法,其中服务器以某个预定义的时间间隔 (pingInterval
) 发送 ping 数据包,客户端有几秒钟的时间 (pingTimeout
) 来响应 pong 数据包。如果服务器在指定的 pingTimeout 内没有收到 pong 数据包,则认为连接已关闭并与客户端断开连接。
https://socket.io/docs/v4/how-it-works/ 请参阅“断线检测”部分