WebSocket通信协议

WebSocket是一种网络通信协议.RFC6455定义了它的通信标准

WebSocket是HTML5开始提供的一种在单个TCP连接上进行全双向通信的协议

HTTP协议是一种无状态的,无连接的,单向的应用层协议.它采用了请求,响应的模式.通信请求只能由客户端发起,服务端对请求做出应答处理.

这种模型有一个弊端:HTTP协议无法实现服务器主动向客户端发起消息

WebSocket的通信模式

1.客户端发送请求 ws://localhost:8080/websocket请求  与服务端建立连接  服务端给与响应(一次握手)

2.接着无论是客户端还是服务端,都可以主动与对方交互

WebSocket协议:

两部分:1.就是上面说的一次握手      2.握手之后无论客户端还是服务端都可以主动数据交互

握手是基于HTTP协议的.

客户端握手发送如下格式

服务端握手响应如下格式

请求头说明

客户端实现:

客户端创建WebSocket对象

var ws=new WebSocket(url);  //url说明: ws://ip地址:端口号/资源名称

相关事件:

WebSocket相关方法   这里只关注send()

服务端实现

Tomcat 7.0.5版本开始支持WebSocket,并且实现了Java WebSocket规范(JSR356) 

Java WebSocket应用由一系列的WebSocketEndpoint组成.Endpoint是一个java对象,代表WebSocket连接的一端,对于服务端,我们可以视为处理具体WebSocket消息的接口,就想Http请求后端时候的Servlet

Endpoint对象:例如有个张三连接服务端,那么服务端生成一个张三Endpoint对象;有个李四连接服务端,那么服务端就生成一个李四Endpoint对象

可以把Endpoint理解成WebSocket的资源

注意:抽象类Endpoint中的方法都有个参数:Session  这个不是HttpSession

我们可以通过两种方式定义Endpoint:

1.编程式,继承javax.websocket.Endpoint并实现其方法.

2.注解式,定义一个POJO,并添加@ServerEndpoint(配置资源路径)注解 及对应的@OnOpen,@OnClose,@OnError注解

Endpoint实例是WebSocket握手时创建,并在客户端与服务端连接过程中有效,最后在连接关闭时结束.在Endpoint接口中明确定义了与其生命周期相关的方法,规范实现者确保生命周期的各个阶段调用实例的相关方法.生命周期方法如下:

服务端WebSocket如何接收客户端发送的数据:(注意下面的Session不是HttpSession)

 通过Session添加MessageHandler消息处理器来接收消息,当采用注解方式定义Endpoint时,我们可以通过@OnMessage注解指定接收消息的方法

服务端如何推送数据给客户端?(注意下面的Session不是HttpSession)

发送消息则由RemoteEndpoint完成,其实例由Session维护,根据使用情况,我们可以通过Session.getBasicRemote获取同步消息发送实例,然后调用其sendXxx()就可以发送消息,可以通过

Session.getAsyncRemote获取异步消息实例.

关键:可以将WebSocket中的Endpoint理解成  Http请求中后端的Servlet,每一个请求过来,都有一个servlet去处理这个请求

Endpoint可以理解成是每一个用户的实例, 一般现在都用注解,那么理解其来也很方便

用@ServerEndpoint注解定义Endpoint   每个Endpoint都是一个客户实例  

用@OnMessage注解指定接收消息的方法

用@OnOpen指定开启一个全新会话时候调用的方法(该方法在客户端与服务端握手成功后调用)

用@OnClose指定会话关闭时的方法

用@OnError指定连接过程出现异常时候调用的方法

服务端代码简单示例:

package com.example.demo.websocket;

import com.example.demo.pojo.Chat;
import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

/**
 * @author hrui
 * @date 2024/4/28 5:06
 */
@ServerEndpoint("/chat")
public class ChatEndpoint {

    private  static Set<ChatEndpoint> webSocketSet=new HashSet<>();

    private Session session;

    @OnMessage
    public void onMessage(String message,Session session) throws IOException{
        System.out.println("接收到的消息是:"+message);
        for(ChatEndpoint chat:webSocketSet){
            //chat.session.getBasicRemote().sendText(message);//这样同时也发送给了自己
            //将消息发送给其他用户
            if(chat!=this){
                chat.session.getBasicRemote().sendText(message);
            }
        }
    }

    @OnOpen
    public void onOpen(Session session){
        this.session=session;
        webSocketSet.add(this);
    }

    @OnClose
    public void onClose(Session session){
        webSocketSet.remove(this);
    }

    @OnError
    public void onError(Session session,Throwable error){
        error.printStackTrace();
    }
}

配置类

@Component
public class WebSocketConfig {
    /**
     * 使用内部容器,需要配置一个Bean
     * @return
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }

}
package com.example.demo.websocket;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import jakarta.websocket.OnClose;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;

import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author hrui
 * @date 2024/4/28 5:50
 */
@ServerEndpoint("/websocket/{serverId}")//serverId是每个客户端的唯一标识
public class ChatEndpoint2 {

    //与客户端通信
    private Session session;
    //客户端标识
    private String serverId;
    //所有连接服务的客户端,线程安全
    private static ConcurrentHashMap<String, ChatEndpoint2> webSocketSet = new ConcurrentHashMap<>();


    @OnOpen
    public void OnOpen(@PathParam(value = "serverId") String serverId, Session session){
        this.session = session;
        this.serverId = serverId;
        //存放所有的客户端连接,serverId唯一标识
        webSocketSet.put(serverId,this);
        System.out.println("客户端连接成功,websocket当前连接数为:"+webSocketSet.size());
    }

    @OnMessage
    public void OnMessage(String message) throws IOException {
        //message是接收到客户端发来的消息
        System.out.println("websocket服务端接收到消息:"+message);
        JSONObject json= JSON.parseObject(message);
        String msg =json.getString("content");         //需要发送的内容
        String receiverId = json.getString("receiverId");  //接收者
        String senderId = json.getString("senderId");  //发送者
        //这里可以写你的业务代码
        //-------------------------
        //根据receiverId 找到对应的客户端
        ChatEndpoint2 service = webSocketSet.get(receiverId);
        //判断接收者是否在线
        if(service != null){
            service.session.getBasicRemote().sendText("需要发送的内容");
        }

    }

    @OnClose
    public void OnClose(){
        webSocketSet.remove(this.serverId);
        System.out.println("客户端退出成功,websocket当前连接数为:"+webSocketSet.size());
    }


}

vue示例

data() {
    return {
		ws: null,       //定义websocket对象
	}
}

//建立websocket服务
initWebSocket() {
	//初始化websocket    userId为会话标识
    const wsuri = 'ws://127.0.0.1:8080/websocket/'+this.userId;
    //连接服务端
    this.ws = new WebSocket(wsuri);
    //指定事件回调
    this.ws.onmessage = this.websocketOnMessage;
    this.ws.onopen = this.websocketOnOpen;
    this.ws.onerror = this.websocketOnError;
    this.ws.onclose = this.websocketClose;
},
//连接建立之后的回调
websocketOnOpen() {
	this.ws.send("握手成功");
	console.log("--------连接已建立!---------")
},
//数据接收
websocketOnMessage(e) {
	console.log("收到了服务端发送的消息"+e)
},
//数据发送
websocketSend(Data) {
	//需要把发送者ID,接收者ID发送给服务端,这样服务端才能知道将消息发给谁
    this.ws.send(Data);
},
//连接建立失败重连
websocketOnError() {
  	this.initWebSocket();
},
//关闭
websocketClose(e) {
 	console.log('断开连接', e);
},



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/579539.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PO框架【自动化测试】

对象&#xff1a;Tpshop商城 需求&#xff1a;更换头像 操作步骤&#xff1a; 个人信息–头像–上传图片–图片确认–确认保存 核心代码&#xff1a; # 进入frame框架[不熟] driver.switch_to.frame(driver.find_element_by_xpath(//*[id"layui-layer-iframe1"]))…

物联网实战--平台篇之(一)架构设计

本项目的交流QQ群:701889554 物联网实战--入门篇https://blog.csdn.net/ypp240124016/category_12609773.html 物联网实战--驱动篇https://blog.csdn.net/ypp240124016/category_12631333.html 一、平台简介 物联网平台这个概念比较宽&#xff0c;大致可以分为两大类&#x…

为什么要学音视频?

一直都在说“科技改变生活”&#xff0c;现实告诉我们这是真的。 随着通信技术和 5G 技术的不断发展和普及&#xff0c;不仅拉近了人与人之间的距离&#xff0c;还拉近了人与物&#xff0c;物与物之间的距离&#xff0c;万物互联也变得触手可及。 基于此背景下&#xff0c;音…

C++面经(简洁版)

1. 谈谈C和C的认识 C在C的基础上添加类&#xff0c;C是一种结构化语言&#xff0c;它的重点在于数据结构和算法。C语言的设计首要考虑的是如何通过一个过程&#xff0c;对输入进行运算处理得到输出&#xff0c;而对C&#xff0c;首先要考虑的是如何构造一个对象&#xff0c;通…

Node.js -- 包管理工具

文章目录 1. 概念介绍2. npm2.1 npm 下载2.2 npm 初始化包2.3 npm 包(1) npm 搜索包(2) npm 下载安装包(3) require 导入npm 包的基本流程 2.4 开发依赖和生产依赖2.5 npm 全局安装(1) 修改windows 执行策略(2) 环境变量Path 2.6 安装包依赖2.7 安装指定版本的包2.8 删除依赖2.…

jenkins教程

jenkins 一、简介二、下载安装三、配置jdk、maven和SSH四、部署微服务 一、简介 Jenkins是一个流行的开源自动化服务器&#xff0c;用于自动化软件开发过程中的构建、测试和部署任务。它提供了一个可扩展的插件生态系统&#xff0c;支持各种编程语言和工具。 Jenkins是一款开…

PotatoPie 4.0 实验教程(27) —— FPGA实现摄像头图像拉普拉斯边缘提取

拉普拉斯边缘提取有什么作用&#xff1f; 拉普拉斯边缘检测是一种常用的图像处理技术&#xff0c;用于检测图像中的边缘和边界。它的主要作用包括&#xff1a; 边缘检测&#xff1a;拉普拉斯算子可以帮助检测图像中的边缘&#xff0c;即图像中亮度快速变化的位置。这些边缘通常…

前端HTML5学习2(新增多媒体标签,H5的兼容性处理)

前端HTML5学习2新增多媒体标签&#xff0c;H5的兼容性处理&#xff09; 分清标签和属性新增多媒体标签新增视频标签新增音频标签新增全局属性 H5的兼容性处理 分清标签和属性 标签&#xff08;HTML元素&#xff09;和属性&#xff0c;标签定义了内容的类型或结构&#xff0c;而…

RocketMQ 消息重复消费

现象 触发消息后&#xff0c;在1s内收到了两次消息消费的日志。 消息消费日志重复&#xff0c;reconsumeTimes0&#xff0c;主机实例也不同&#xff0c;说明是同一条消息被消费了两次 分析 生产者发送消息的时候使用了重试机制&#xff0c;发送消息后由于网络原因没有收到MQ…

永磁同步电机PMSM负载状态估计simulink模型

永磁同步电机PMSM负载状态估计simulink模型&#xff0c;龙伯格观测器&#xff0c;各种卡尔曼滤波器&#xff0c;矢量控制&#xff0c;坐标变换&#xff0c;永磁同步电机负载转矩估计&#xff0c;pmsm负载转矩测量&#xff0c;负载预测&#xff0c;转矩预测的matlab/simulink仿真…

【C++】---STL容器适配器之queue

【C】---STL容器适配器之queue 一、队列1、队列的性质 二、队列类1、队列的构造2、empty()3、push()4、pop()5、size()6、front()7、back() 三、队列的模拟实现1、头文件&#xff08;底层&#xff1a;deque&#xff09;2、测试文件3、底层&#xff1a;list 一、队列 1、队列的…

【NR RedCap】Release 18标准中对5G RedCap的增强

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G技术研究。 博客内容主要围绕…

R语言贝叶斯方法在生态环境领域中的应用

贝叶斯统计已经被广泛应用到物理学、生态学、心理学、计算机、哲学等各个学术领域&#xff0c;其火爆程度已经跨越了学术圈&#xff0c;如促使其自成统计江湖一派的贝叶斯定理在热播美剧《The Big Bang Theory》中都要秀一把。贝叶斯统计学即贝叶斯学派是一门基本思想与传统基于…

使用微信开发者工具模拟微信小程序定位

哈喽&#xff0c;各位同僚们&#xff0c;我们平时在测试微信小程序的时候&#xff0c;如果小程序中有获取定位或者地图的功能&#xff0c;测试场景中常常需要去模拟不同的位置&#xff0c;例如我们模拟在电子围栏的外面、里面和边界区域等。那么&#xff0c;我们如何在模拟微信…

[笔试训练](八)

目录 022&#xff1a;求最小公倍数 023&#xff1a;数组中的最长连续子序列 024&#xff1a;字母收集 022&#xff1a;求最小公倍数 求最小公倍数_牛客题霸_牛客网 (nowcoder.com) 题目&#xff1a; 题解&#xff1a; 求最小公倍数公式&#xff1a;lcm(a,b)a*b/gcd(a,b)&am…

创建springboot项目的问题

IDEA搭建spring boot时报错Error: Request failed with status code 400 Could not find artifact org.springframework.boot:spring-boot-starter-parent:pom:3.2.5.RELEASE in alimaven (http://maven.aliyun.com/nexus/content/repositories/central/) 原因是父级依赖的版本…

Web前端开发 小实训(一) 成绩分类统计

用于学生web前端开发课程实训练习&#xff0c;掌握基本语法和数据类型 实训目的 使用分支语句&#xff0c;完成分数统计与等级对比,通过输入框输入分数&#xff0c;可以根据分数多少划分等级。 参考思路&#xff1a; 分析题目&#xff1a;根据输入分数进行等级划分。 操作过…

yolov8 dll 编译

1. 每次用yolo v8 都要用python &#xff0c;对于我这种写软件的太不方便了&#xff0c;下面尝试编译dll 调用, 我已经有做好的模型.best.pt 参考视频方法: yolov8 TensorRT C 部署_哔哩哔哩_bilibili 【yolov8】tensorrt部署保姆级教程&#xff0c;c版_哔哩哔哩_bilibili 需…

怎么做视频二维码更方便?在线一键生成视频活码二维码

现在经常会发现很多的二维码可以用来展示视频内容&#xff0c;通过这种方式来实现视频的快速分享与传播。二维码是一种成本低传播快的内容传播方式&#xff0c;很多的内容都可以通过生成二维码的方式来分享给其他人&#xff0c;可以同时扫描相同的二维码来获取内容&#xff0c;…

电脑的无用设置功能(建议关闭)

目录 1、传递优化 ​2、常规​ 3、电源 1、传递优化 2、常规3、电源