当前位置: 首页 > article >正文

Websocket——化神篇

WebSocket机制

WebSocket 是 HTML5 一种新的协议。它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯,它建立在 TCP 之上,同 HTTP 一样通过 TCP 来传输数据,但是它和 HTTP 最大不同是:
WebSocket 是一种双向通信协议,在建立连接后,WebSocket 服务器和 Browser/Client Agent 都能主动的向对方发送或接收数据,就像 Socket 一样;
WebSocket 需要类似 TCP 的客户端和服务器端通过握手连接,连接成功后才能相互通信。

和socket区别

Socket其实并不是一个协议,而是为了方便使用TCP或UDP而抽象出来的一层,是位于应用层和传输控制层之间的一组接口。

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
当两台主机通信时,必须通过Socket连接,Socket则利用TCP/IP协议建立TCP连接。TCP连接则更依靠于底层的IP协议,IP协议的连接则依赖于链路层等更低层次。
WebSocket则是一个典型的应用层协议。
区别
Socket是传输控制层协议,WebSocket是应用层协议。

前世今生

众所周知,Web 应用的交互过程通常是客户端通过浏览器发出一个请求,服务器端接收请求后进行处理并返回结果给客户端,客户端浏览器将信息呈现,这种机制对于信息变化不是特别频繁的应用尚可,但对于实时要求高、海量并发的应用来说显得捉襟见肘,尤其在当前业界移动互联网蓬勃发展的趋势下,高并发与用户实时响应是 Web 应用经常面临的问题,比如金融证券的实时信息,Web 导航应用中的地理位置获取,社交网络的实时消息推送等。

消息推送常用方式

轮询方式
在这里插入图片描述
1.轮询是一种客户端与服务器之间实时通信的技术手段。客户端定期发送请求来查询服务器是否有新数据或事件,并将响应返 回给客户端。如果服务器有新的数据或事件,则将其返回给客户端;如果没有,则返回一个空响应。客户端收到响应后,可 以处理数据或事件,并根据需要继续发送下一个请求。
2.长轮询是一种改进的轮询技术,其主要目的是降低轮询过程中的资源消耗和延迟。长轮询的基本原理是客户端发送一个 HTTP请求给服务器,并保持连接打开,直到服务器有新的数据或事件时才返回响应给客户端。在这期间,服务器会一直保持连接打开,直到超时或有新数据或事件(HTTP1.1版本就是这个性质长连接
SSE(服务器发送事件)

SSE在服务器和客户端之间打开一个单向通道
服务端响应的不再是一次性的数据包,而是text/event-stream类型的数据流信息
服务器有数据变更时将数据流式传输到客户端
在这里插入图片描述

WebSocket
在这里插入图片描述
在这里插入图片描述
客户端API
在这里插入图片描述
服务端 API
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
Endpoint示例

@ServerEndpoint("/chat")
@Component
public class ChatEndpoint {
    @OnOpen
    //连接建立时被调用
    public void onOpen(Session session, EndpointConfig config){}
    @OnMessage
    //接收到客户端发送的数据时被调用
    public void onMessage(String message){}
    @OnClose
    //连接关闭时被调用
    public void onClose(Session session){}
}

ChatEndpoint类通过实现WebSocket协议,用于处理客户端的连接、消息传递和关闭事件。

在线聊天室实现

流程分析
在这里插入图片描述
消息格式
在这里插入图片描述
引入坐标

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

编写配置类

@Configuration
public class WebsocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

通过在配置类中定义一个 ServerEndpointExporter 的 @Bean 方法,Spring 会自动创建一个 ServerEndpointExporter 实例,并将其加入到 Spring 容器中。Spring框架会在启动时通过这个实例自动扫描项目中所有使用@ServerEndpoint注解的类,并将它们注册为WebSocket端点。
编写配置类,用于获取 HttpSession 对象

public class GetHttpSessionConfig extends ServerEndpointConfig.Configurator {

    @Override
    public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
        //获取HttpSession对象
        HttpSession httpSession = (HttpSession) request.getHttpSession();
        //将httpSession对象保存起来
        sec.getUserProperties().put(HttpSession.class.getName(),httpSession);
    }
}

GetHttpSessionConfig类用于在WebSocket握手过程中获取HTTP会话(HttpSession)对象,并将其保存到用户属性中,以便在WebSocket会话中使用。
在这里插入图片描述
ChatEndpoint类

package com.itheima.ws;

import com.alibaba.fastjson.JSON;
import com.itheima.config.GetHttpSessionConfig;
import com.itheima.utils.MessageUtils;
import com.itheima.ws.pojo.Message;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpSession;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @version v1.0
 * @ClassName: ChatEndpoint
 * @Description: TODO(一句话描述该类的功能)
 * @Author: 黑马程序员
 */
@ServerEndpoint(value = "/chat",configurator = GetHttpSessionConfig.class)
@Component
public class ChatEndpoint {

    private static final Map<String,Session> onlineUsers = new ConcurrentHashMap<>();

    private HttpSession httpSession;

    /**
     * 建立websocket连接后,被调用
     * @param session
     */
    @OnOpen
    public void onOpen(Session session, EndpointConfig config) {
        //1,将session进行保存
        this.httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
        String user = (String) this.httpSession.getAttribute("user");
        onlineUsers.put(user,session);
        //2,广播消息。需要将登陆的所有的用户推送给所有的用户
        String message = MessageUtils.getMessage(true,null,getFriends());
        broadcastAllUsers(message);
    }

    public Set getFriends() {
        Set<String> set = onlineUsers.keySet();
        return set;
    }

    private void broadcastAllUsers(String message) {
        try {
            //遍历map集合
            Set<Map.Entry<String, Session>> entries = onlineUsers.entrySet();
            for (Map.Entry<String, Session> entry : entries) {
                //获取到所有用户对应的session对象
                Session session = entry.getValue();
                //发送消息
                session.getBasicRemote().sendText(message);
            }
        } catch (Exception e) {
            //记录日志
        }
    }

    /**
     * 浏览器发送消息到服务端,该方法被调用
     *
     * 张三  -->  李四
     * @param message
     */
    @OnMessage
    public void onMessage(String message) {
        try {
            //将消息推送给指定的用户
            Message msg = JSON.parseObject(message, Message.class);
            //获取 消息接收方的用户名
            String toName = msg.getToName();
            String mess = msg.getMessage();
            //获取消息接收方用户对象的session对象
            Session session = onlineUsers.get(toName);
            String user = (String) this.httpSession.getAttribute("user");
            String msg1 = MessageUtils.getMessage(false, user, mess);
            session.getBasicRemote().sendText(msg1);
        } catch (Exception e) {
            //记录日志
        }
    }

    /**
     * 断开 websocket 连接时被调用
     * @param session
     */
    @OnClose
    public void onClose(Session session) {
        //1,从onlineUsers中剔除当前用户的session对象
        String user = (String) this.httpSession.getAttribute("user");
        onlineUsers.remove(user);
        //2,通知其他所有的用户,当前用户下线了
        String message = MessageUtils.getMessage(true,null,getFriends());
        broadcastAllUsers(message);
    }
}

@ServerEndpoint(value = “/chat”, configurator = GetHttpSessionConfig.class) 这行代码声明了一个 WebSocket 端点,客户端可以通过 /chat 路径与之建立连接,并且在握手阶段使用 GetHttpSessionConfig 类来进行自定义配置。

该类是Spring Boot应用中的一个WebSocket端点,它用于处理聊天功能。主要功能包括:

  • 在用户建立WebSocket连接时保存用户信息和会话对象。
  • 当用户发送消息时,将消息转发给指定的接收用户。
  • 当用户断开连接时,从在线用户列表中移除用户并通知其他用户。
  • 使用GetHttpSessionConfig配置器来获取HTTP会话中的用户信息。
    userController
package com.itheima.controller;

import com.itheima.pojo.Result;
import com.itheima.pojo.User;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpSession;


@RestController
@RequestMapping("user")
public class UserController {

    /**
     * 登陆
     * @param user 提交的用户数据,包含用户名和密码
     * @param session
     * @return
     */
    @PostMapping("/login")
    public Result login(@RequestBody User user, HttpSession session) {
        Result result = new Result();
        if(user != null && "123".equals(user.getPassword())) {
            result.setFlag(true);
            //将数据存储到session对象中
            session.setAttribute("user",user.getUsername());
        } else {
            result.setFlag(false);
            result.setMessage("登陆失败");
        }
        return result;
    }

    /**
     * 获取用户名
     * @param session
     * @return
     */
    @GetMapping("/getUsername")
    public String getUsername(HttpSession session) {

        String username = (String) session.getAttribute("user");
        return username;
    }
}

Result

package com.itheima.pojo;

import lombok.Data;

/**
 * @version v1.0
 * @ClassName: Result
 * @Description: 用来封装http请求的响应数据
 * @Author: 黑马程序员
 */
@Data
public class Result {
    private boolean flag;
    private String message;
}

User

package com.itheima.pojo;

import lombok.Data;

/**
 * @version v1.0
 * @ClassName: User
 * @Description: 接收登录请求的数据
 * @Author: 黑马程序员
 */
@Data
public class User {

    private String userId;
    private String username;
    private String password;
}

MessageUtils

package com.itheima.utils;

import com.alibaba.fastjson.JSON;
import com.itheima.ws.pojo.ResultMessage;

/**
 * @version v1.0
 * @ClassName: MessageUtils
 * @Description: 封装json格式消息的工具类
 * @Author: 黑马程序员
 */
public class MessageUtils {

    public static String getMessage(boolean isSystemMessage,String fromName, Object message) {

        ResultMessage result = new ResultMessage();
        result.setSystem(isSystemMessage);
        result.setMessage(message);
        if(fromName != null) {
            result.setFromName(fromName);
        }
        return JSON.toJSONString(result);
    }
}

Message

package com.itheima.ws.pojo;

import lombok.Data;

/**
 * @version v1.0
 * @ClassName: Message
 * @Description: 用于封装浏览器发送给服务端的消息数据
 * @Author: 黑马程序员
 */
@Data
public class Message {
    private String toName;
    private String message;
}

ResultMessage

package com.itheima.ws.pojo;

import lombok.Data;

/**
 * @version v1.0
 * @ClassName: ResultMessage
 * @Description: 用来封装服务端给浏览器发送的消息数据
 * @Author: 黑马程序员
 */
@Data
public class ResultMessage {

    private boolean isSystem;
    private String fromName;
    private Object message;//如果是系统消息是数组
}

http://www.kler.cn/a/416552.html

相关文章:

  • Spring Cloud(Kilburn 2022.0.2版本)系列教程(五) 服务网关(SpringCloud Gateway)
  • Linux学习笔记11 系统启动初始化,服务和进程管理(下)
  • ubuntu20配置mysql注意事项
  • 如何启动 Docker 服务:全面指南
  • redis命令 及 redis 常见的数据结构
  • Spring Boot开发——整合JPA配置多数据源
  • 解决 PyTorch Upsample 属性错误:方法与最佳实践
  • 在并发情况下,Elasticsearch如果保证读写一致?
  • redis中的哨兵
  • vue3.0 根据富文本html页面生成压缩包(含视频在线地址、图片在线地址、前端截图、前端文档)
  • NeurIPS 2024 有效投稿达 15,671 篇,数据集版块内容丰富
  • MySQL 性能:基准测试工具包(BMK-kit)
  • Java开发工程师最新面试题库系列——Java基础部分(附答案)
  • 深入浅出:开发者如何快速上手Web3生态系统
  • C++调用QML函数的两种方法
  • 计算机毕业设计Python+LSTM天气预测系统 AI大模型问答 vue.js 可视化大屏 机器学习 深度学习 Hadoop Spark
  • C++基础:muduo库学习记录
  • 格网法计算平面点云面积(matlab版本)
  • 考试排名(一)(结构体专题)
  • 2024年11月一区SCI-Alpha evolution-附Matlab免费代码
  • javax.net.ssl.SSLHandshakeException: Received fatal alert: protocol_version
  • DM-VIO(ROS)+t265配置运行记录(ubuntu18.04+ros melodic)
  • Maven - 优雅的管理多模块应用的统一版本号
  • 利用Java爬虫精准获取淘宝商品详情的探索之旅
  • Mac 环境下类Xshell 的客户端介绍
  • 周期性移动模式地铁乘客流量预测