如何ping websocket服务器,以免因闲置而断开连接。

10

我正在使用websocket-client连接到一个WebSocket服务器。我连接的服务器似乎希望我定期向它发送ping,否则它会将我断开连接。

当我连接到服务器时,它会立即向我发送此消息:

0{"sid":"SomeID","upgrades":[],"pingInterval":25000,"pingTimeout":60000}

这似乎告诉我ping间隔和ping超时时间。我注意到我的websocket客户端在连接后大约1分25秒后会不断地断开连接。如果将这些数字相加,60秒+25秒,就得到了1分25秒。所以看来我需要定期ping这个服务器,以便它不会让我断开连接。

如何ping服务器?我尝试了ws.ping(),但似乎不存在。我需要以某种格式向服务器发送数据吗?还是有一些内置的ping命令?

    websocket.enableTrace(True)
    ws = websocket.WebSocketApp("wss://socket.serverssite.com/socket.io/?transport=websocket",
                                on_message=on_message,
                                on_error=on_error,
                                on_close=on_close)
    ws.run_forever()

注意:我在node中创建了一个websocket客户端(不是python),它在1分25秒后仍未关闭。因此,似乎它具有一些内置的ping,这个python websocket客户端没有...
------------------------编辑-------------------------
尝试设置ping_interval和ping_timeout,但它仍然关闭:
2017-11-06 12:49:14.340037--------------------- Doing stuff
2017-11-06 12:49:14.340309--------------------- Doing stuff
send: '\x89\x80\\\xd9\xc4\xdd'
2017-11-06 12:49:19.341680--------------------- Doing stuff
2017-11-06 12:49:19.341958--------------------- Doing stuff
send: '\x89\x80\xd9\x06\xef\xa8' 
2017-11-06 12:49:24.343426--------------------- Doing stuff
2017-11-06 12:49:24.343769--------------------- Doing stuff
send: "\x89\x80\xe6\x92'\xb8" 
send: '\x88\x823\xfb$\xd10\x13' 
closed (Here is where the closed method gets called, the server shut me down)

只是出于好奇,我假设你也可以访问服务器代码,对吧?下面的答案似乎是一个服务端设置。 - icedwater
2个回答

8

从源代码中 -

def run_forever(self, sockopt=None, sslopt=None,
                ping_interval=0, ping_timeout=None,
                http_proxy_host=None, http_proxy_port=None,
                http_no_proxy=None, http_proxy_auth=None,
                skip_utf8_validation=False,
                host=None, origin=None)

指定 ping_interval 应该适合你的需求。

ws.run_forever(ping_interval=70, ping_timeout=10)

1
谢谢。由于某种原因,服务器仍在关闭它。我想知道这个库发送的ping是否不是服务器所期望的。我看到它发送了一些东西,比如:send: '\x89\x80f\x7fLt',send: '\x89\x8000\xf8\x85',send: '\x88\x82-l\xb8\x00.\x84',但之后它仍然会在1分25秒后关闭。 - LampShade
1
我在使用Python时遇到了类似的情况,过去几周尝试了各种解决方案,但仍未能解决。你有找到答案吗? - Thomas
@Thomas,你解决了吗?最好在创建实例时提供自定义ping消息的选项(例如heartbeat_message={"message"="PING"})。我已经考虑过自己fork该项目并添加它。 - leo_cape
@leo_cape,我已经修改了代码,但对我的情况没有帮助。在我的情况下,可能是由于Python无法跟上大量数据的到来。我计划让脚本在多台计算机上运行,处理部分数据,看看是否有所帮助。 - Thomas
谢谢Thomas,对于我的情况,我添加了一个无限循环,并加入了一些保障措施,手动发送符合服务器需要的ping格式的消息。感觉有点hacky,但似乎没有其他解决方法,除了分叉,我看不到其他的方法。干杯! - leo_cape

3

我曾经遇到过类似的问题,通过使用 socket.io 向服务器发送 2 来解决了它。我当时正在使用 asyncio 和 websockets 库,并使用以下代码来防止 websocket 关闭:

await ws.send('2')

使用标准的WebSocket库会有类似的操作。

完整解释

服务器是socket.io,这意味着您需要在pingTimeout内向服务器发送一个2的ping消息。来自socket.io

编码 - 包 一个经过编码的数据包可以是 UTF-8 字符串或二进制数据。字符串的包编码格式如下所示: ... 0 open 当新传输打开时(重新检查)从服务器发送。 1 close 请求关闭此传输,但不关闭连接本身。 2 ping 由客户端发送。服务器应该回答一个包含相同数据的pong数据包。 ... 超时 客户端必须使用作为握手的一部分(通过打开数据包发送)发送的pingTimeout和pingInterval来确定服务器是否无响应。 客户端发送一个ping包。如果在pingTimeout内未收到任何数据包类型,则客户端认为套接字已断开连接。如果实际收到pong数据包,则客户端会在等待pingInterval之前再次发送ping数据包。 由于这两个值在服务器和客户端之间共享,因此当服务器在pingTimeout + pingInterval时间内未收到任何数据时,服务器也能检测到客户端是否变得无响应。

1
这不是正确的 ping 方法。调用 ws.send( '2' ) 将发送字符串 '2'。你需要使用 ws.ping( payload ),其中 payload 是一个随机字符字符串。参见:https://github.com/websocket-client/websocket-client/blob/562808fe25f4f922e3422b99eb00aa53d9734ba0/websocket/_core.py#L287 - Stefan
对我来说,ws.send('2') 在客户端上可以工作,但如果我发送其他字符串,例如 ws.send('test'),则服务器端会显示 解析错误(parse error) - Martin J
3
在使用基于Websockets协议的Socket.IO协议时,这是正确的ping方式。 - Guillaume Brunerie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接