使用Websocket实现消息推送(心跳)
Posted ttdevs
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用Websocket实现消息推送(心跳)相关的知识,希望对你有一定的参考价值。
0x00 心跳
本来以为写完了,结果最近和一个同事在讨论心跳的事情,这里再做一个补充。先说我的结论:
- WebSocket协议已经设计了心跳,这个功能可以到达检测链接是否可用
- 心跳是用来检测链接是否可用的,不一定支持携带数据,可要看具体实现
- 如果非要心跳中带上复杂数据,那这个可作为应用层的一个功能自己去实现。
0x01 WebSocket协议的控制帧
上一篇的最后简单提到了心跳,下面是对websocket协议控制帧的描述:
5.5. Control Frames
Control frames are identified by opcodes where the most significant
bit of the opcode is 1. Currently defined opcodes for control frames
include 0x8 (Close), 0x9 (Ping), and 0xA (Pong). Opcodes 0xB-0xF are
reserved for further control frames yet to be defined.
Control frames are used to communicate state about the WebSocket.
Control frames can be interjected in the middle of a fragmented
message.
All control frames MUST have a payload length of 125 bytes or less
and MUST NOT be fragmented.
- Ping的协议头是0x9,Pong的协议头是0xA
- 控制帧最大载荷为125bytes且不能拆分
0x02 WebSocket协议的心跳
下面再来看看对心跳的规定:
5.5.2. Ping
The Ping frame contains an opcode of 0x9.
A Ping frame MAY include "Application data".
// 注:Ping帧中可能会携带数据
Upon receipt of a Ping frame, an endpoint MUST send a Pong frame in
response, unless it already received a Close frame. It SHOULD
respond with Pong frame as soon as is practical. Pong frames are
discussed in Section 5.5.3.
// 注:在收到Ping帧后,端点必须发送Pong帧响应,除非已经收到了Close帧。在实际中应尽可能快的响应。
An endpoint MAY send a Ping frame any time after the connection is
established and before the connection is closed.
NOTE: A Ping frame may serve either as a keepalive or as a means to
verify that the remote endpoint is still responsive.
5.5.3. Pong
The Pong frame contains an opcode of 0xA.
Section 5.5.2 details requirements that apply to both Ping and Pong
frames.
A Pong frame sent in response to a Ping frame must have identical
"Application data" as found in the message body of the Ping frame
being replied to.
// 注:在响应Ping帧的的Pong帧中,必须携和被响应的Ping帧中相同的数据。
If an endpoint receives a Ping frame and has not yet sent Pong
frame(s) in response to previous Ping frame(s), the endpoint MAY
elect to send a Pong frame for only the most recently processed Ping
frame.
从上面的描述我们可以得到如下结论:
- 心跳包中可能会携带数据
- 当收到Ping帧的时候需要立即返回一个Pong帧
- 在连接建立之后,随时都可以发送Ping帧
- 心跳是用来测试链接是否存在和对方是否在线
- 在响应Ping帧的的Pong帧中,必须携和被响应的Ping帧中相同的数据
0x03 测试
和之前一样,自己本地搭建的服务器,用的库是 org.java_websocket
。
在其源码中我们可以找到这样一段:
package org.java_websocket;
public abstract class WebSocketAdapter implements WebSocketListener
...
public void onWebsocketPing(WebSocket conn, Framedata f)
FramedataImpl1 resp = new FramedataImpl1(f);
resp.setOptcode(Opcode.PONG);
conn.sendFrame(resp);
public void onWebsocketPong(WebSocket conn, Framedata f)
...
客户端也可以使用这个库,相同的逻辑,代码也是这一份。
然后我们再换一个库,com.squareup.okhttp3:okhttp-ws:3.4.2
,他的实现如下:
package okhttp3.internal.ws;
public abstract class RealWebSocket implements WebSocket
...
public RealWebSocket(boolean isClient, BufferedSource source, BufferedSink sink, Random random,
final Executor replyExecutor, final WebSocketListener listener, final String url)
this.listener = listener;
writer = new WebSocketWriter(isClient, sink, random);
reader = new WebSocketReader(isClient, source, new FrameCallback()
@Override public void onMessage(ResponseBody message) throws IOException
listener.onMessage(message);
@Override public void onPing(final Buffer buffer)
replyExecutor.execute(new NamedRunnable("OkHttp %s WebSocket Pong Reply", url)
@Override protected void execute()
try
writer.writePong(buffer);
catch (IOException ignored)
);
@Override public void onPong(Buffer buffer)
listener.onPong(buffer);
@Override public void onClose(final int code, final String reason)
readerSentClose = true;
replyExecutor.execute(new NamedRunnable("OkHttp %s WebSocket Close Reply", url)
@Override protected void execute()
peerClose(code, reason);
);
);
...
在处理Ping帧的时候,也是将协议字段改为Pong然后返回。
对心跳的测试代码已经上传到Github:ttdevs
WebSocketActivity.java
WebSocketOKActivity.java
SocketServer.java
在实际的测试中,可能会遇到一些异常,比如在我们自己的生产环境:当客户端发送带了简单数据的Ping帧后,服务器立马返回Pong帧,但是它会将携带的数据丢弃。这个就是服务端的问题了。
参考:
RFC6455: https://datatracker.ietf.org/doc/rfc6455/
以上是关于使用Websocket实现消息推送(心跳)的主要内容,如果未能解决你的问题,请参考以下文章
uni-app中websocket的使用 断开重连、心跳机制