如何检查 Websocket 连接是不是存在

Posted

技术标签:

【中文标题】如何检查 Websocket 连接是不是存在【英文标题】:How to check is a Websocket connection is alive如何检查 Websocket 连接是否存在 【发布时间】:2018-05-01 13:21:20 【问题描述】:

我有一个到服务器的 websocket 连接:

import javax.websocket.*;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

@ClientEndpoint
public class WebsocketExample 

    private Session userSession;

    private void connect() 

        try 
            WebSocketContainer container = ContainerProvider.getWebSocketContainer();
            container.connectToServer(this, new URI("someaddress"));
         catch (DeploymentException | URISyntaxException | IOException e) 
            e.printStackTrace();
        
    

    @OnOpen
    public void onOpen(Session userSession) 
        // Set the user session
        this.userSession = userSession;
        System.out.println("Open");
    

    @OnClose
    public void onClose(Session userSession, CloseReason reason) 
        this.userSession = null;
        System.out.println("Close");
    

    @OnMessage
    public void onMessage(String message) 
        // Do something with the message
        System.out.println(message);
    

一段时间后,我似乎不再收到来自服务器的任何消息,但尚未调用 onClose 方法。

例如,如果我在最后五分钟内没有收到任何消息,我希望有一种计时器至少会记录错误(并且最好尝试重新连接)。当我收到新消息时,计时器将被重置。

我该怎么做?

【问题讨论】:

你为什么不打乒乓球? tools.ietf.org/html/rfc6455#section-5.5.2NOTE: A Ping frame may serve either as a keepalive or as a means to verify that the remote endpoint is still responsive. 有标准的方法吗? 阅读规范。这就是它应该如何工作的方式。看看你的 websocket 框架是如何实现的,或者自己写。由于您没有具体说明您的 websocket 代码使用什么,什么框架/库等。如果它是该框架的一部分,我无法为您搜索。 【参考方案1】:

这就是我所做的。我通过jetty更改了javax.websocket并实现了ping调用:

import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.client.WebSocketClient;

import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@WebSocket
public class WebsocketExample 

    private Session userSession;
    private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);

    private void connect() 
        try 
            SslContextFactory sslContextFactory = new SslContextFactory();
            WebSocketClient client = new WebSocketClient(sslContextFactory);
            client.start();
            client.connect(this, new URI("Someaddress"));
         catch (Exception e) 
            e.printStackTrace();
        
    

    @OnWebSocketConnect
    public void onOpen(Session userSession) 
        // Set the user session
        this.userSession = userSession;
        System.out.println("Open");

        executorService.scheduleAtFixedRate(() -> 
                    try 
                        String data = "Ping";
                        ByteBuffer payload = ByteBuffer.wrap(data.getBytes());
                        userSession.getRemote().sendPing(payload);
                     catch (IOException e) 
                        e.printStackTrace();
                    
                ,
                5, 5, TimeUnit.MINUTES);
    

    @OnWebSocketClose
    public void onClose(int code, String reason) 
        this.userSession = null;
        System.out.println("Close");
    

    @OnWebSocketMessage
    public void onMessage(String message) 
        // Do something with the message
        System.out.println(message);
    

编辑:这只是一个 ping 示例...我不知道是否所有服务器都应该通过 pong 来应答...

Edit2:这是处理 pong 消息的方法。诀窍不是监听 String 消息,而是 Frame 消息:

@OnWebSocketFrame
@SuppressWarnings("unused")
public void onFrame(Frame pong) 
    if (pong instanceof PongFrame) 
        lastPong = Instant.now();
    

为了管理服务器超时,我将定时任务修改如下:

scheduledFutures.add(executorService.scheduleAtFixedRate(() -> 
                    try 
                        String data = "Ping";
                        ByteBuffer payload = ByteBuffer.wrap(data.getBytes());
                        userSession.getRemote().sendPing(payload);

                        if (lastPong != null
                                && Instant.now().getEpochSecond() - lastPong.getEpochSecond() > 60) 
                            userSession.close(1000, "Timeout manually closing dead connection.");
                        

                     catch (IOException e) 
                        e.printStackTrace();
                    
                ,
                10, 10, TimeUnit.SECONDS));

...并在 onClose 方法中处理重新连接

【讨论】:

session.getBasicRemote().sendPing(null); 怎么样?【参考方案2】:

您应该通过实现一个心跳系统来解决这个问题,该系统一侧发送ping,一侧回复pong。几乎每个 websocket 客户端和服务器(据我所知)在内部都支持此功能。这个 ping/pong 帧可以从双方发送。我通常在服务器端实现它,因为我通常知道它比客户端有更好的存活机会(我的观点)。如果客户长时间不发回乒乓球,我知道连接已经死了。在客户端,我检查相同:如果服务器长时间没有发送 ping 消息,我知道连接已死。

如果你使用的库中没有实现 ping/pong(我认为 javax websocket 有它),你可以为此制定自己的协议。

【讨论】:

谢谢。我不拥有服务器,所以我不能编写任何代码。当我从客户端向服务器发送 ping 消息时,我从来没有得到 pong 答案 您能否提供有关如何发送 ping 帧的代码? 在我上面的回答中 好的,我检查了答案。如果session.getBasicRemote().sendPing(null); 不起作用,那么您只需联系后端团队并与他们核实即可。

以上是关于如何检查 Websocket 连接是不是存在的主要内容,如果未能解决你的问题,请参考以下文章

使用 websocket 的 Web 应用程序“无法建立连接。接收端不存在。”

如何检查Java中是不是存在互联网连接?

JAVA建立连接后如何检查数据库是不是存在

Laravel 8 如何连接 2 个表并检查值是不是存在

订阅频道时如何检查websocket用户是不是经过身份验证?

如何使用 asyncio 检查 Websocket 数据是不是为空