WebSocket 长连接

这篇具有很好参考价值的文章主要介绍了WebSocket 长连接。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

介绍

WebSocket 是一种网络传输协议,可在单个 TCP 连接上进行全双工通信,位于 OSI 模型的应用层

早期,很多网站为了实现推送技术,所用的技术都是轮询(也叫短轮询)。轮询是指由浏览器每隔一段时间向服务器发出 HTTP 请求,然后服务器返回最新的数据给客户端。常见的轮询方式分为轮询与长轮询

在这种情况下,HTML5 定义了 WebSocket 协议,能更好的节省服务器资源和带宽(因为 HTTP 请求可能会包含较长的头部,但真正有效的可能只有小部分),并且能够更实时地进行通讯。Websocket 使用 ws 或 wss 的统一资源标志符(URI),其中 wss 表示使用了 TLS 的 Websocket,ws 与 HTTP 协议有良好的兼容性

WS 协议和 WSS 协议两个均是 WebSocket 协议,两者一个是非安全的,一个是安全的。就好比 HTTP 协议和 HTTPS 协议的差别,非安全的没有证书,安全的如同 HTTPS 一样需要 SSL 证书,证书当然是配置在 ng 上的

普遍认为,WebSocket 的优点有如下几点:

  • 较少的控制开销:在连接创建后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小
  • 更强的实时性:由于协议是全双工的,所以服务器可以随时主动给客户端下发数据。相对于 HTTP 请求需要等待客户端发起请求服务端才能响应,延迟明显更少
  • 保持连接状态:与 HTTP 不同的是,WebSocket 需要先创建连接,这就使得其成为一种有状态的协议,之后通信时可以省略部分状态信息
  • 更好的二进制支持:WebSocket 定义了二进制帧,相对 HTTP,可以更轻松地处理二进制内容

依赖

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-websocket</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.websocket</groupId>
            <artifactId>javax.websocket-api</artifactId>
        </dependency>

配置类

配置类里将我们写的规则类注册进去

WebSocketHandlerRegistry 的 addHandler 方法是将 WebSocketHandler 和对应的 URL 路径注册到 WebSocketHandlerRegistry 中,以供后续的 WebSocket 连接请求进行匹配和处理。当有 WebSocket 连接请求到达时,WebSocketHandlerRegistry 会根据请求的 URL 路径找到对应的 WebSocketHandler,并将请求交给该 Handler 进行处理

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Autowired
    private MyWsHandler myWsHandler;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(myWsHandler, "/ws/voice/remind.json")
                //允许跨域
                .setAllowedOrigins("*");
    }
}

实现

继承 AbstractWebSocketHandler,实现方法,即可自定义在连接、传入、中断等时候分别可以执行的操作。这里我们选择继承其子类 TextWebSocketHandler 来处理文本消息

@Component
@Slf4j
public class MyWsHandler extends TextWebSocketHandler {

    /**
     * 这个是管理 session 的类
     */
    @Resource
    WsSessionManager wsSessionManager;

    /**
     * 定义了客户端链接服务器的时候会执行什么操作
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        log.info("建立ws连接");
        wsSessionManager.add(session.getId(), session);
    }

    /**
     * handleTextMessage 方法是用来处理收到的文本消息的。当客户端发送文本消息到服务器端时,服务器端会调用 handleTextMessage 方法来处理该消息。在该方法中,开发者可以编写自定义的业务逻辑来处理文本消息,例如解析消息内容、调用其他服务进行处理等。同时,开发者还可以在该方法中向客户端发送文本消息,以实现双向通信
     */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        log.info("发送文本消息");
        // 获得客户端传来的消息
        String payload = message.getPayload();
        log.info("server 接收到消息 " + payload);
        session.sendMessage(new TextMessage("server 发送给的消息 " + payload + ",发送时间:" + LocalDateTime.now().toString()));
    }

    /**
     * 出现错误与异常的时候执行的操作
     */
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) {
        log.error("异常处理");
        wsSessionManager.removeAndClose(session.getId());
    }

    /**
     * 连接中断后执行的操作
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        log.info("关闭ws连接");
        wsSessionManager.removeAndClose(session.getId());
    }
}

我们能在建立连接的时候将一些信息放在 session 里,WebSocketSession::getAttributes 是一个获取 WebSocketSession 对象的属性的方法,这个方法返回一个 Map<String, Object> 对象,其中包含了 WebSocketSession 对象中所有的属性。这些属性是在 WebSocketSession 对象创建时通过 WebSocketHandler 的 afterConnectionEstablished 方法存储的

在 afterConnectionEstablished 方法中,WebSocketSession对象被创建并存储在内存中。此时,WebSocketSession对象的属性可以通过调用WebSocketSession的setAttribute方法进行设置。setAttribute方法接受两个参数,第一个参数是属性的名称,第二个参数是属性的值。

获取 session(经过实践业务上不需要这么用,不必保留该对象进行操作)

我们的重头戏就是对 session 的操作,因为可以在用户登录登出时做记录,因此我们可以知道用户是否在线,从而实现在用户在线时对其发消息这个操作


/**
 * 管理 session 的类,完全可以在 MyWsHandler 中实现,但是我们应该尽可能将 SESSION_POOL 的操作与 handler 的定义剥离开来
 *
 * @author yifanxie.xie
 */
@Slf4j
@Component
public class WsSessionManager {
    /**
     * 保存连接 session 的地方
     */
    private static final ConcurrentHashMap<String, WebSocketSession> SESSION_POOL = new ConcurrentHashMap<>();

    /**
     * 添加 session
     *
     * @param key
     */
    public void add(String key, WebSocketSession session) {
        // 添加 session
        SESSION_POOL.put(key, session);
    }

    /**
     * 删除并同步关闭连接
     *
     * @param key
     */
    public void removeAndClose(String key) {
        WebSocketSession session = SESSION_POOL.remove(key);
        if (session != null) {
            try {
                // 关闭连接
                session.close();
            } catch (IOException e) {
                log.error("在关闭链接时出现异常", e);
            }
        }
    }

    /**
     * 获得 session
     *
     * @param key
     * @return
     */
    public WebSocketSession get(String key) {
        return SESSION_POOL.get(key);
    }
}

这里我们使用会话 id 来当做 map 的 key,但是在对特定用户发消息的时候谁会用 id 来对应用户啊。我们能在建立连接的时候用一些业务属性的唯一标识来作为 key,从而确定用户

nginx 配置的各种问题

请求 400

如果使用 webSocket 并且使用 nginx 做转发的话,会报以下错误:

failed: Error during WebSocket handshake: Unexpected response code: 400

这个问题其实是由于客户端错误或不存在的域名导致的,如果代码没有错误的话,可能是 ng 的配置不对

在 https下使用 ws,提示不安全

Mixed Content: The page at 'https://www.joshua317.com/1.html' was loaded over HTTPS, but attempted to connect to the insecure WebSocket endpoint 'ws://im.joshua317.com/'. This request has been blocked; this endpoint must be available over WSS.

Uncaught DOMException: Failed to construct 'WebSocket': An insecure WebSocket connection may not be initiated from a page loaded over HTTPS.

问题出现在 nginx 的配置文件,需要修改 nginx.conf 文件。在 linux 终端中敲入 vim /etc/nginx/nginx.conf,找到 location 这个位置,配置文件如下所示:

server {
        listen       80;
        server_name  school.godotdotdot.com;
        charset utf-8;

        location / {
            proxy_pass http://127.0.0.1:3000;
            proxy_set_header Host $host;
            proxy_http_version 1.1; 
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_connect_timeout 60;
            proxy_read_timeout 600;
            proxy_send_timeout 600;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

    }

其中最重要的是下面这三行

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

其中第一行是告诉 nginx 使用 HTTP/1.1 通信协议,这是 websoket 必须要使用的协议

第二行和第三行告诉 nginx,当它想要使用 WebSocket 时,响应 http 升级请求

在不支持 ssl 的情况下,直接用 wss 链接

index.ts:8 WebSocket connection to 'ws://im.joshua317.com/' failed: Error in connection establishment: net::ERR_SSL_VERSION_OR_CIPHER_MISMATCH
或者
failed: Error in connection establishment: net::ERR_CERT_COMMON_NAME_INVALID

因为 HTTPS 是基于 SSL 依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密,所以在 HTTPS 站点调用某些非 SSL 验证的资源时浏览器可能会阻止,简单来说就是验证不过

所以这个问题其实是 ng 不支持 https 导致的,我们加个证书来让它支持

server {
    listen 80;
    server_name im.joshua317.com;
    #调整成自己的证书即可,重点重点重点
    ssl_certificate /usr/local/nginx/conf/ssl/xxxx.crt;
    ssl_certificate_key /usr/local/nginx/conf/ssl/xxxx.key;
    ssl_session_timeout 5m;
     #调整成自己的即可,重点重点重点
    ssl_ciphers xxxxxxxxxxxxx;

如果我们设置 location 不正确的时候

failed: Error during WebSocket handshake: Unexpected response code: 404

综上,websocket 的状态码和 http 的其实差不多文章来源地址https://www.toymoban.com/news/detail-720202.html

到了这里,关于WebSocket 长连接的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 持久双向通信网络协议-WebSocket-入门案例实现demo

    1 介绍 WebSocket 是基于 TCP 的一种新的 网络协议 。它实现了浏览器与服务器全双工通信——浏览器和服务器只需要完成一次握手,两者之间就可以创建 持久性 的连接, 并进行 双向 数据传输。 HTTP协议和WebSocket协议对比: HTTP是 短连接 (一次响应完即消除) WebSocket是 长连接

    2024年01月16日
    浏览(45)
  • 网络通信协议-HTTP、WebSocket、MQTT的比较与应用

    在今天的数字化世界中,各种通信协议起着关键的作用,以确保信息的传递和交换。HTTP、WebSocket 和 MQTT 是三种常用的网络通信协议,它们各自适用于不同的应用场景。本文将比较这三种协议,并探讨它们的主要应用领域。 HTTP (超文本传输协议) HTTP  是最常见的协议之一

    2024年02月05日
    浏览(58)
  • Java连接websocket优雅断线、重连功能

          为了实现优雅重连和重试,您需要在代码中添加一些逻辑来处理连接失败或断开连接的情况。 实现代码如下:

    2024年02月10日
    浏览(36)
  • 使用java完成WebSocket自动主动断开连接功能

    一个页面实时刷新的功能,页面上的数据状态可能会随着操作实时改变,所以每个用户在使用的时候都希望能看到数据的最新状态。 我想到了两种解决方法:1.轮循,2.WebSocket 我们这里采用的是WebSocket来解决问题 WebSocket在建立连接后,如果不是人为操作的话,他不会主动地进

    2024年02月07日
    浏览(52)
  • java连接websocket服务器并发送消息

    一、用python快速启动一个websocker服务器 二、使用java连接并发送消息

    2024年02月11日
    浏览(44)
  • 前端面试:【网络协议与性能优化】HTTP/HTTPS、TCP/IP和WebSocket

    嗨,亲爱的Web开发者!在构建现代Web应用时,了解网络协议是优化性能和确保安全性的关键。本文将深入探讨HTTP/HTTPS、TCP/IP和WebSocket这三个网络协议,帮助你理解它们的作用以及如何优化Web应用的性能。 1. HTTP/HTTPS协议: HTTP(超文本传输协议): HTTP是用于在Web上传输数据的

    2024年02月11日
    浏览(48)
  • Java网络Socket编程-websocket

    实现一个用于监测 WebSocket 连接状态的线程类,其作用是通过创建一个 WebSocket 客户端,连接到指定的 WebSocket 地址,并监测连接的状态。 代码中的 WebSocketThread 类继承自 Thread ,意味着它可以在单独的线程中执行。该线程类使用 Tyrus 提供的 @ClientEndpoint 注解来标识这是一个

    2024年02月08日
    浏览(46)
  • WebSocket 报java.io.IOException: 远程主机强迫关闭了一个现有的连接。

    在客户端强制关闭时,或者窗口强制关闭时,后端session没有关闭。 有时还会报:java.io.EOFException: 这个异常 前端心跳没有收到信息,还在心跳。 所以在  @OnClose ,@OnError 在这两个方法中,不管是关闭还是发生未知错误,都关闭session

    2024年02月09日
    浏览(58)
  • vue项目中使用websocket连接后立马断开(websocket连接后瞬间断开)

    问题原因(连接后断连的原因):前端给后端传递Authourization(token验证)时,后端需要接收处理并设置响应标头,不然就容易出现上叙错误; 解决方法: 1、传递参数和验证权限; 2、后端处理后前端收到的响应标头;  然后,连接后断连的问题就解决啦!感觉有用,就一键

    2024年02月11日
    浏览(46)
  • Go语言github.com/gorilla/websocket框架websocket协议通信实战

          websocket是实际开发中比较常用的应用层协议,本文利用github.com/gorilla/websocket框架进行websocket通信实战。 目录 1.下载github.com/gorilla/websocket 2.websocket服务端 3.websocket Go客户端 4.websocket 网页客户端 5.运行结果展示 go get github.com/gorilla/websocket 服务器:  Go语言客户端:  We

    2024年02月16日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包