Websocket原理和实践

本文涉及的产品
数据传输服务 DTS,数据迁移 small 3个月
推荐场景:
MySQL数据库上云
数据传输服务 DTS,数据同步 small 3个月
推荐场景:
数据库上云
数据传输服务 DTS,数据同步 1个月
简介: WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

一、概述

1.websocket是什么?

WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

WebSocket与HTTP协议不同,它使用了独立的端口,并且在建立连接后,不需要在每次数据传输时重新建立连接。这使得它特别适合于实时应用程序,例如聊天,在线游戏和股票交易等,这些应用程序需要高速,双向的数据传输。

WebSocket协议是HTML5标准的一部分,因此它可以在现代浏览器中使用。WebSocket API可以在JavaScript中使用,这样就可以在网页上直接使用WebSocket进行通信。

2.websocket和HTTP对比

WebSocket是一种与HTTP不同的协议。两者都位于OSI模型的应用层,并且都依赖于传输层的TCP协议。 虽然它们不同,但是RFC 6455中规定:it is designed to work over HTTP ports 80 and 443 as well as to support HTTP proxies and intermediaries(WebSocket通过HTTP端口80和443进行工作,并支持HTTP代理和中介),从而使其与HTTP协议兼容。 为了实现兼容性,WebSocket握手使用HTTP Upgrade头[1]从HTTP协议更改为WebSocket协议。

其具体的区别如下:

  1. 通信方式不同: HTTP协议是一种请求-响应协议,客户端发送请求给服务器,服务器返回响应。而WebSocket协议是一种全双工协议,客户端和服务器都可以主动发送消息。
  2. 链接状态不同: HTTP协议是无状态的,每次请求都是独立的。而WebSocket协议是有状态的,建立了连接之后,客户端和服务器之间可以维持长连接。
  3. 数据传输不同: HTTP协议是基于文本的,而WebSocket协议是基于二进制的。
  4. 延迟不同: HTTP协议每次请求都需要建立连接,等待响应,传输数据,释放连接,这整个过程都需要一些时间。而WebSocket协议只需要建立一次连接,之后就可以高效地进行数据传输,所以延迟更小。

3.websocket的优势和劣势

  1. 较少的控制开销。在连接建立后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小。在不包含扩展的情况下,对于服务器到客户端的内容,此头部大小只有2至10字节(和数据包长度有关);对于客户端到服务器的内容,此头部还需要加上额外的4字节的掩码。相对于HTTP请求每次都要携带完整的头部,此项开销显著减少了。
  2. 更强的实时性。由于协议是全双工的,所以服务器可以随时主动给客户端下发数据。相对于HTTP请求需要等待客户端发起请求服务端才能响应,延迟明显更少;即使是和Comet等类似的长轮询比较,其也能在短时间内更多次地传递数据。
  3. 保持连接状态。与HTTP不同的是,Websocket需要先建立连接,这就使得其成为一种有状态的协议,之后通信时可以省略部分状态信息。而HTTP请求可能需要在每个请求都携带状态信息(如身份认证等)。
  4. 更好的二进制支持。Websocket定义了二进制帧,相对HTTP,可以更轻松地处理二进制内容。
  5. 可以支持扩展。Websocket定义了扩展,用户可以扩展协议、实现部分自定义的子协议。如部分浏览器支持压缩等。
  6. 更好的压缩效果。相对于HTTP压缩,Websocket在适当的扩展支持下,可以沿用之前内容的上下文,在传递类似的数据时,可以显著地提高压缩率。

4.websocket应用场景

  1. 在线聊天:实现实时的文本聊天功能。
  2. 直播和视频会议:实现实时的音频和视频传输。
  3. 游戏:实现实时的游戏状态同步和控制。
  4. 实时监控和控制:实现实时的硬件监控和控制。
  5. 数据可视化:实现实时的数据可视化和分析。
  6. 在线协作:实现实时的文档协作和编辑。
  7. 智能家居:实现实时的智能家居控制。
  8. 推送消息:实现实时的推送消息功能

这些场景中,WebSocket的实时通信特性使得WebSocket成为了一种理想的选择。

利用websocket处理在线聊天业务的常见流程:

img

二、原理

1.WebSocket通信原理和机制

HTTP通信方式是请求-响应的单工通信,他的通信模式是一问一答式的,如果需要服务端主动发送消息给客户端,他是做不到的。

Websocket的通信方式是全双工模式,无论客户端还是服务端,都能够自主发起通信。但是WebSocket 是独立的、建立在TCP上的协议。Websocket 通过 HTTP/1.1 协议的101状态码进行握手。为了建立Websocket连接,需要通过浏览器发出请求,之后服务器进行回应,这个过程通常称为“握手”(Handshaking)。握手是通过HTTP协议完成的,但是一旦建立连接后,数据传输将使用WebSocket协议。

img

WebSocket通信的流程如下:

  1. 客户端发送一个HTTP请求,请求的目的是为了要建立一个WebSocket连接。
  2. 服务器收到请求后,给出一个HTTP响应,并升级连接到WebSocket协议。
  3. 客户端和服务器之间建立一个WebSocket连接。
  4. 客户端和服务器之间可以进行双向通信,发送文本和二进制数据。
  5. 当客户端或服务器关闭连接时,WebSocket连接也会关闭。

与 HTTP 通信不同的是,WebSocket 通信是基于TCP的,所以它是一个持久连接。它允许服务器主动发送信息给客户端,而不是等待客户端的请求。这使得 WebSocket 通信成为了实现实时应用的理想选择。

建立了websocket连接,请求的URL会以ws://xx开始,在请求头中会有upgrade:websocket标志,说明该请求是websocket请求。

image-20230817164249524

后续客户端和服务器的消息通信会在同一个回话中展示。

image-20230817164304308

2.Java Websocket API介绍

与HTTP不同,websocket的通信具有生命周期,此生命周期由websocket协议本身支撑,具体实现websocket的语言都需要支持。

  1. 建立连接事件:此事件发生在端点上建立新连接时并且在任何其他事件发生之前

  2. 发送消息事件:此事件接收WebSocket对话中另一端发送的消息。它可以发生在WebSocket端点接收了打开事件之后且在接收关闭事件关闭连接之前的任意时刻

  3. 出现错误事件:此事件在WebSocket连接或者端点发生错误时产生

  4. 连接关闭事件:此事件表示WebSocket端点的连接或者端点目前正在部分的关闭,它可以由参与连接的任意一个端点发出

    img

针对Java来说,websocket的事件都会有对应的方法处理,其主要通过编程式端点服务来处理:

  • @OnOpen:处理建立连接的事件;
    @OnOpen
    public void init(Session session, EndpointConfig config) {
   
   

    }
  • @OnMessage:处理建立连接后收发消息的事件;
    @OnMessage
    public void handleTextMessage(String textMessage) {
   
   

    }
-----------
    @OnMessage
    public void handleBinaryMessage(byte[] messageData, Session session) {
   
   

    }
  • @OnError:处理消息时候发生错误的处理;
  • @OnClose:处理连接关闭的事件;

三、实践

1.Springboot集成Websocket

通过Springboot集成Websocket有两种实现,分别是基于注解的实现和实现接口的方式,下面分别展开:

1.1.基于注解

添加pom依赖

在pom配置文件中引入spring-boot-starter-websocket

<!--websocket-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

在配置类中增加websocket配置Bean

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * @author yangnk
 * @desc
 * @date 2023/08/15 00:30
 **/
@Configuration
public class WebSocketConfig {
   
   
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
   
   
        return new ServerEndpointExporter();
    }
}

实现websocketServer

实现一个websocketserver,需要添加@ServerEndpoint("/websocket/{name}")注解,该注解需要加在类上,表示请求websocket的url地址,再实现websocket各个声明周期的注解的方法。

声明ConcurrentHashMap webSocketMap用来保存websocket连接的session。

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author yangnk
 * @desc
 * @date 2023/08/15 00:31
 **/

/**
 * @ServerEndpoint 注解的作用
 *
 * @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
 * 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
 */
@Slf4j
@Component
@ServerEndpoint("/websocket/{name}")
public class WebSocketServer {
   
   

    /**
     * 与某个客户端的连接对话,需要通过它来给客户端发送消息
     */
    private Session session;

    /**
     * 标识当前连接客户端的用户名
     */
    private String name;

    /**
     * 用于存所有的连接服务的客户端,这个对象存储是安全的
     * 注意这里的kv,设计的很巧妙,v刚好是本类 WebSocket (用来存放每个客户端对应的MyWebSocket对象)
     */
    private static ConcurrentHashMap<String, WebSocketServer> webSocketMap = new ConcurrentHashMap<>();


    /**
     * 连接建立成功调用的方法
     * session为与某个客户端的连接会话,需要通过它来给客户端发送数据
     */
    @OnOpen
    public void OnOpen(Session session, @PathParam(value = "name") String name){
   
   

        log.info("----------------------------------");
        this.session = session;
        this.name = name;
        // name是用来表示唯一客户端,如果需要指定发送,需要指定发送通过name来区分
        webSocketMap.put(name,this);
        log.info("[WebSocket] 连接成功,当前连接人数为:={}", webSocketMap.size());
        GroupSending(name+" 来了");
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void OnClose(){
   
   
        webSocketMap.remove(this.name);
        log.info("[WebSocket] 退出成功,当前连接人数为:={}", webSocketMap.size());
        GroupSending(name+" 走了");
    }

    /**
     * 收到客户端消息后调用的方法
     */
    @OnMessage
    public void OnMessage(String message_str){
   
   
        log.info("[WebSocket] 收到消息:{}",message_str);
        //判断是否需要指定发送,具体规则自定义
        //message_str的格式 TOUSER:user2;message:aaaaaaaaaaaaaaaaaa;
        if(message_str.indexOf("TOUSER") == 0){
   
   
            //取出 name和message的值
            String[] split = message_str.split(";");
            String[] split1 = split[0].split(":");
            String[] split2 = split[1].split(":");
            String name = split1[1];
            String message = split2[1];
            //指定发送
            AppointSending(name,message);
        }else{
   
   
            //群发
            GroupSending(message_str);
        }
    }

    /**
     * 发生错误时调用
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error){
   
   
        log.info("发生错误");
        error.printStackTrace();
    }

    /**
     * 群发
     * @param message
     */
    public void GroupSending(String message){
   
   
        for (String name : webSocketMap.keySet()){
   
   
            try {
   
   
                webSocketMap.get(name).session.getBasicRemote().sendText(name + ":" + message);
            }catch (Exception e){
   
   
                e.printStackTrace();
            }
        }
    }

    /**
     * 指定发送
     * @param name
     * @param message
     */
    public void AppointSending(String name,String message){
   
   
        try {
   
   
            webSocketMap.get(name).session.getBasicRemote().sendText(name + ":" + message);
        }catch (Exception e){
   
   
            e.printStackTrace();
        }
    }
}

控制器Controller调用方法

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
/**
 * @author yangnk
 * @desc
 * @date 2023/08/15 00:32
 **/

@RestController("web_Scoket_system")
@RequestMapping("/api/socket")
public class SystemController {
   
   

    @Autowired
    WebSocketServer socketServer;

    //推送数据接口
    @ResponseBody
    @RequestMapping("/socket/push/{name}")
    public String pushToWeb(@PathVariable String name, @RequestHeader String message) {
   
   
        Map<String,Object> result = new HashMap<>();
        socketServer.AppointSending(name, message);
        result.put("name", name);
        result.put("msg", message);
        return result.toString();
    }
}

验证结果

可以在在线websocket测试网站:https://www.bejson.com/httputil/websocket/ 进行验证。

image-20230816114918213

1.2.实现接口

TODO

2.完整代码

https://github.com/yangnk/SpringBoot_Learning/tree/master/SpringBootExample/src/main/java/com/yangnk/webSocketExample

3.常见问题

  1. websocket的默认超时实践为30min,如果需要长时间保持websocket连接,就需要设置保活机制。

TODO

  1. 补充通过实现接口的方式来使用websocket;

参考资料

  1. WebSocket:https://zh.wikipedia.org/zh-cn/WebSocket (官网)
  2. springboot整合websocket最基础入门使用教程详解:https://developer.aliyun.com/article/983651 (websocket demo写的比较详细,具有实操性)
  3. SpringBoot 集成 WebSocket,实现后台向前端推送信息:https://cloud.tencent.com/developer/article/1830998 (websocket demo写的不错)
  4. 一文了解WebSocket及Springboot集成WebSocket:https://developer.aliyun.com/article/1152716 (讲了比较多原理方面的东西)
  5. springboot整合webSocket(看完即入门):https://juejin.cn/post/7139334773356363783
  6. WebSocket使用介绍,看这篇就够了:https://juejin.cn/post/7244407068098560037#heading-6 (websocket的进阶使用)
  7. Java WebSocket 基础 生命周期:https://blog.csdn.net/sakuragio/article/details/98673473 (讲了非常详细的websocket事件和API使用情况)
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
Sqoop 企业级大数据迁移方案实战
Sqoop是一个用于在Hadoop和关系数据库服务器之间传输数据的工具。它用于从关系数据库(如MySQL,Oracle)导入数据到Hadoop HDFS,并从Hadoop文件系统导出到关系数据库。 本课程主要讲解了Sqoop的设计思想及原理、部署安装及配置、详细具体的使用方法技巧与实操案例、企业级任务管理等。结合日常工作实践,培养解决实际问题的能力。本课程由黑马程序员提供。
目录
相关文章
|
缓存 移动开发 网络协议
WebSocket 协议原理抓包分析
WebSocket 协议原理抓包分析
520 0
|
6月前
|
移动开发 缓存 网络协议
Websocket协议原理及Ws服务器代码实现
Websocket协议原理及Ws服务器代码实现
|
25天前
|
安全 JavaScript 网络协议
WebSocket通信协议基础原理与安全威胁
WebSocket通信协议基础原理与安全威胁
67 0
|
23天前
|
监控 小程序 前端开发
小程序全栈开发中的WebSocket实时通信实践
【10月更文挑战第3天】随着移动互联网的发展,小程序因便捷的用户体验和社交传播能力,成为企业拓展业务的新渠道。本文探讨了小程序全栈开发中的WebSocket实时通信实践,包括其实时通信、长连接及双向通信的特点,并通过实时聊天、推送、游戏和监控等功能的实现,展示了WebSocket在小程序中的应用。开发者需注意安全性、性能及兼容性等问题,以保障小程序的稳定运行和用户体验。
41 7
|
2月前
|
安全 JavaScript 网络协议
WebSocket通信协议基础原理与安全威胁
WebSocket通信协议基础原理与安全威胁
49 7
|
3月前
|
监控 小程序 安全
小程序全栈开发中的WebSocket实时通信实践是一种高效的开发模式。
随着移动互联网的发展,小程序成为企业拓展业务的新渠道。WebSocket作为一种实时通信协议,可在小程序中实现如实时聊天、推送、游戏等功能。它支持客户端与服务器间的全双工长连接通信,优于传统HTTP。开发者需注意安全、性能及兼容性等问题,以优化体验并保障稳定运行。掌握WebSocket有助于提升小程序功能性与用户体验。
48 1
|
4月前
|
JavaScript 前端开发 网络协议
从理论到实践:全面剖析Python Web应用中的WebSocket实时通信机制
【7月更文挑战第17天】WebSocket在实时Web应用中扮演重要角色,提供全双工通信,减少延迟。本文详述了Python中使用`websockets`库创建服务器的步骤,展示了一个简单的echo服务器示例,监听8765端口,接收并回显客户端消息。客户端通过JavaScript与服务器交互,实现双向通信。了解WebSocket的握手、传输和关闭阶段,有助于开发者有效利用WebSocket提升应用性能。随着实时需求增长,掌握WebSocket技术至关重要。
284 6
|
4月前
|
监控 前端开发 JavaScript
构建高效实时应用:Python WebSocket在前后端分离架构中的实践
【7月更文挑战第18天】WebSocket助力实时Web应用,通过一次握手建立持久连接,解决HTTP实时性问题。Python中可用Flask-SocketIO创建WebSocket服务器,前端JavaScript使用Socket.IO库连接。确保安全可采用HTTPS、认证及跨域限制。示例代码展示如何实现双向实时通信。
107 4
|
5月前
|
监控 网络协议 Java
Java中的WebSocket应用与实践
Java中的WebSocket应用与实践
|
6月前
|
监控 小程序 前端开发
小程序全栈开发中的WebSocket实时通信实践
【4月更文挑战第12天】本文探讨了小程序全栈开发中WebSocket实时通信的实践,WebSocket作为实现双向实时通信的协议,其长连接特性和双向通信能力在实时聊天、推送、游戏和监控等场景中发挥关键作用。开发者需注意安全性、性能和兼容性问题,以优化用户体验并确保小程序稳定运行。通过掌握WebSocket,开发者能提升小程序的功能性和用户体验。
136 0