• WebSocket--1.协议解析


    目录

    一.概念

    二.建立流程

    三.四大事件

    五.js中建立ws链接

     六.springboot中进行ws连接

    1.首先,添加WebSocket的依赖到你的Spring Boot项目中。

     2.接下来,创建一个WebSocket处理器

    3.最后,创建一个配置类,注册该WebSocket处理器:

    七.使用@ServerEndpoint注解实现

     八.群聊私聊案例

    九.文字和图片消息的处理


    一.概念

    WebSocket是一种在单个TCP连接上进行全双工通信的协议。它通过在客户端和服务器之间建立持久的连接,实现了服务器端可以主动推送消息给客户端的功能,而不需要客户端发起请求。

    WebSocket协议的主要特点包括:

    全双工通信:客户端和服务器可以同时发送和接收消息,无需等待对方的回应。

    基于事件驱动:当有新的消息到达时,服务器可以主动推送消息给客户端,而不需要客户端发起请求。

    较低的带宽消耗:WebSocket协议使用较少的 HTTP 头信息,因此带宽消耗较小。

    较低的延迟:WebSocket协议采用长连接的方式,减少了连接建立的时间和数据传输的延迟。

    跨域通信:WebSocket协议支持跨域通信,可以在不同的域名下进行通信。

    适用于实时应用:由于WebSocket协议的特性,它非常适用于需要实时更新的应用程序,如在线聊天、实时数据更新等。

    WebSocket协议的使用需要具备以下条件:

    1.客户端和服务器都需要支持WebSocket协议。

    2.客户端和服务器之间需要建立一个WebSocket连接。

    3.客户端和服务器需要通过WebSocket协议进行通信,发送和接收消息。

    目前,WebSocket协议已被广泛应用于Web应用程序、移动应用程序和即时通讯等领域

    二.建立流程

    建立WebSocket连接的流程如下:

    1. 创建WebSocket对象:在客户端代码中创建一个WebSocket对象,用于与服务器建立连接。

    2. 发起握手请求:客户端WebSocket对象发送一个HTTP升级请求,请求将协议从HTTP更改为WebSocket。

    3. 服务器回应握手请求:服务器收到握手请求后,返回一个HTTP升级响应,确认请求已成功。

    4. 建立WebSocket连接:一旦握手成功,建立WebSocket连接。此时,双方可以通过WebSocket对象发送和接收消息。

    5. 数据传输:客户端和服务器之间可以通过WebSocket对象进行双向数据传输。

    6. 关闭连接:当WebSocket连接不再需要时,可以通过调用WebSocket对象的close()方法来关闭连接。

    WebSocket协议建立报文格式如下:

    1. 客户端发送握手请求报文给服务器:
    1. GET /chat HTTP/1.1
    2. Host: server.example.com
    3. Upgrade: websocket
    4. Connection: Upgrade
    5. Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
    6. Sec-WebSocket-Version: 13
    1. 服务器返回握手响应报文给客户端:
    1. HTTP/1.1 101 Switching Protocols
    2. Upgrade: websocket
    3. Connection: Upgrade
    4. Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

    其中,Sec-WebSocket-Key字段是客户端随机生成的16字节的字符串,服务器收到后会加上一个固定的字符串"258EAFA5-E914-47DA-95CA-C5AB0DC85B11",然后通过SHA-1算法计算出Sec-WebSocket-Accept字段的值。这个值用于验证客户端发送的请求是否合法。

    WebSocket传输的数据格式是经过封装的二进制或文本数据。

    在建立WebSocket连接之后,客户端和服务器都可以互相发送消息。发送的消息可以是文本类型或二进制类型。

    对于文本类型的消息,客户端和服务器发送的数据格式如下:

    1. Opcode: TEXT (0x1)
    2. Payload length: 7 (7字节长度)
    3. Payload data: "Hello!"

    这个消息报文包含了一个Opcode字段,指示消息类型为文本类型;一个Payload length字段,指示消息的长度为7字节;一个Payload data字段,保存了具体的消息内容。

    对于二进制类型的消息,格式类似,只是Opcode字段值改为BINARY (0x2)。

    除了普通的消息类型外,WebSocket还支持PING和PONG类型的消息,用于心跳检测。PING消息的格式如下:

    1. Opcode: PING (0x9)
    2. Payload length: 4 (4字节长度)
    3. Payload data: "ping"

    服务器接收到PING消息后会回复一个PONG消息,PONG消息的格式如下:

    1. Opcode: PONG (0xA)
    2. Payload length: 4 (4字节长度)
    3. Payload data: "pong"

    三.四大事件

    WebSocket有四个主要的事件:

    1. 连接建立事件onopen):在客户端与服务器成功建立连接时触发。可以用来发送初始数据或进行认证。

    2. 消息接收事件onmessage):当服务器向客户端发送消息时触发。可以通过此事件获取服务器发送的数据。

    3. 连接关闭事件onclose):在客户端与服务器的连接关闭时触发。可以进行一些清理工作,比如释放资源或重新连接。

    4. 错误事件onerror):当 WebSocket 连接发生错误时触发。可以通过此事件处理连接错误,例如连接失败或消息发送失败等。

    五.js中建立ws链接

    在JavaScript中建立WebSocket连接可以使用WebSocket对象。以下是建立WebSocket连接的步骤:

    1. 创建一个WebSocket对象:使用new WebSocket()构造函数创建一个WebSocket对象。需要传入WebSocket的URL作为参数。例如:
    const socket = new WebSocket('wss://example.com/socket');
    1. 事件处理:WebSocket对象有几个事件处理函数,用于处理不同的WebSocket状态和消息。

      • onopen事件:当WebSocket连接成功建立时触发。
      • onmessage事件:当接收到服务器发送的消息时触发。
      • onclose事件:当WebSocket连接关闭时触发。
      • onerror事件:当发生WebSocket错误时触发。

      你可以使用下面的语法进行事件处理:

      1. socket.onopen = function() {
      2. // 连接成功
      3. };
      4. socket.onmessage = function(event) {
      5. // 接收到消息
      6. const message = event.data;
      7. };
      8. socket.onclose = function(event) {
      9. // 连接关闭
      10. const code = event.code;
      11. const reason = event.reason;
      12. };
      13. socket.onerror = function(error) {
      14. // 处理错误
      15. };
    2. 发送和接收消息:可以使用WebSocket对象的send()方法发送消息,使用onmessage事件处理函数接收消息。例如:

      1. socket.send('Hello, server!');
      2. socket.onmessage = function(event) {
      3. const message = event.data;
      4. console.log('Received message: ' + message);
      5. };
    3. 关闭连接:可以使用WebSocket对象的close()方法关闭连接。例如:

      socket.close();

     六.springboot中进行ws连接

    1.首先,添加WebSocket的依赖到你的Spring Boot项目中。

      在pom.xml文件中,加入以下依赖:

    1. <dependencies>
    2. <dependency>
    3. <groupId>org.springframework.bootgroupId>
    4. <artifactId>spring-boot-starter-websocketartifactId>
    5. dependency>
    6. dependencies>

     2.接下来,创建一个WebSocket处理器

    ,用于处理WebSocket相关的事件:

    1. import org.springframework.stereotype.Component;
    2. import org.springframework.web.socket.CloseStatus;
    3. import org.springframework.web.socket.TextMessage;
    4. import org.springframework.web.socket.WebSocketHandler;
    5. import org.springframework.web.socket.WebSocketMessage;
    6. import org.springframework.web.socket.WebSocketSession;
    7. @Component
    8. public class MyWebSocketHandler implements WebSocketHandler {
    9. @Override
    10. public void afterConnectionEstablished(WebSocketSession session) throws Exception {
    11. System.out.println("WebSocket连接已建立");
    12. session.sendMessage(new TextMessage("你已成功连接到WebSocket服务器"));
    13. }
    14. @Override
    15. public void handleMessage(WebSocketSession session, WebSocketMessage message) throws Exception {
    16. System.out.println("收到消息:" + message.getPayload());
    17. session.sendMessage(new TextMessage("服务端已收到您的消息:" + message.getPayload()));
    18. }
    19. @Override
    20. public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
    21. System.out.println("WebSocket连接已关闭");
    22. }
    23. @Override
    24. public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
    25. System.out.println("WebSocket传输错误");
    26. }
    27. @Override
    28. public boolean supportsPartialMessages() {
    29. return false;
    30. }
    31. }

    在这个处理器中,我们实现了WebSocketHandler接口,并重写了其中的方法。在afterConnectionEstablished方法中,当Websocket连接成功建立时,会打印一条消息,并发送一条欢迎消息给客户端。在handleMessage方法中,当收到客户端发送的消息时,会打印消息内容,并返回一条回复消息给客户端。在afterConnectionClosed方法中,当Websocket连接关闭时,会打印一条消息。

    3.最后,创建一个配置类,注册该WebSocket处理器:

    1. import org.springframework.context.annotation.Configuration;
    2. import org.springframework.web.socket.config.annotation.EnableWebSocket;
    3. import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
    4. import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
    5. @Configuration
    6. @EnableWebSocket
    7. public class WebSocketConfig implements WebSocketConfigurer {
    8. @Override
    9. public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
    10. registry.addHandler(new MyWebSocketHandler(), "/websocket")
    11. .setAllowedOrigins("*");
    12. }
    13. }

    在这个配置类中,我们使用@EnableWebSocket注解启用WebSocket支持,并通过registerWebSocketHandlers方法注册了我们之前创建的WebSocket处理器MyWebSocketHandler,指定了WebSocket的访问路径为/websocket并设置允许的跨域访问。

    七.使用@ServerEndpoint注解实现

    以下是使用@ServerEndpoint注解实现WebSocket的四大事件的示例代码:

    1. @ServerEndpoint("/websocket")
    2. public class MyWebSocketEndpoint {
    3. private static Set sessionSet = new HashSet<>();
    4. @OnOpen
    5. public void onOpen(Session session) {
    6. System.out.println("WebSocket连接已建立");
    7. sessionSet.add(session);
    8. try {
    9. session.getBasicRemote().sendText("你已成功连接到WebSocket服务器");
    10. } catch (IOException e) {
    11. e.printStackTrace();
    12. }
    13. }
    14. @OnMessage
    15. public void onMessage(String message, Session session) {
    16. System.out.println("收到消息:" + message);
    17. try {
    18. session.getBasicRemote().sendText("服务端已收到您的消息:" + message);
    19. } catch (IOException e) {
    20. e.printStackTrace();
    21. }
    22. }
    23. @OnClose
    24. public void onClose(Session session) {
    25. System.out.println("WebSocket连接已关闭");
    26. sessionSet.remove(session);
    27. }
    28. @OnError
    29. public void onError(Throwable error) {
    30. System.err.println("WebSocket错误:" + error.getMessage());
    31. }
    32. }

    在这个示例中,通过@ServerEndpoint("/websocket")注解声明了一个WebSocket端点,并使用@OnOpen@OnMessage@OnClose注解分别实现了连接建立、接收消息和连接关闭事件。

    需要注意的是,Session对象用于表示客户端与服务器之间的WebSocket连接,通过它可以向客户端发送消息。在示例中,我们使用一个HashSet来保存所有连接的Session对象。

     八.群聊私聊案例

    下面是一个简单的示例,展示了如何在WebSocket中实现群聊和私聊功能:

    1. import javax.websocket.*;
    2. import javax.websocket.server.ServerEndpoint;
    3. import java.io.IOException;
    4. import java.util.HashSet;
    5. import java.util.Set;
    6. @ServerEndpoint("/websocket")
    7. public class ChatWebSocketEndpoint {
    8. private static Set sessionSet = new HashSet<>();
    9. @OnOpen
    10. public void onOpen(Session session) {
    11. System.out.println("WebSocket连接已建立");
    12. sessionSet.add(session);
    13. try {
    14. session.getBasicRemote().sendText("你已成功连接到WebSocket服务器");
    15. } catch (IOException e) {
    16. e.printStackTrace();
    17. }
    18. }
    19. @OnMessage
    20. public void onMessage(String message, Session session) {
    21. String[] messageParts = message.split(":", 2);
    22. String sender = session.getId();
    23. if (messageParts.length > 1) { // 私聊
    24. String receiver = messageParts[0].trim();
    25. String privateMessage = messageParts[1].trim();
    26. sendPrivateMessage(sender, receiver, privateMessage);
    27. } else { // 群聊
    28. sendGroupMessage(sender, message);
    29. }
    30. }
    31. private void sendGroupMessage(String sender, String message) {
    32. for (Session session : sessionSet) {
    33. try {
    34. session.getBasicRemote().sendText("[" + sender + "]: " + message);
    35. } catch (IOException e) {
    36. e.printStackTrace();
    37. }
    38. }
    39. }
    40. private void sendPrivateMessage(String sender, String receiver, String message) {
    41. for (Session session : sessionSet) {
    42. if (session.getId().equals(receiver)) {
    43. try {
    44. session.getBasicRemote().sendText("[私聊]" + sender + ": " + message);
    45. } catch (IOException e) {
    46. e.printStackTrace();
    47. }
    48. return;
    49. }
    50. }
    51. try {
    52. Session senderSession = getSessionById(sender);
    53. senderSession.getBasicRemote().sendText("私聊对象不存在");
    54. } catch (IOException e) {
    55. e.printStackTrace();
    56. }
    57. }
    58. private Session getSessionById(String sessionId) {
    59. for (Session session : sessionSet) {
    60. if (session.getId().equals(sessionId)) {
    61. return session;
    62. }
    63. }
    64. return null;
    65. }
    66. @OnClose
    67. public void onClose(Session session) {
    68. System.out.println("WebSocket连接已关闭");
    69. sessionSet.remove(session);
    70. }
    71. @OnError
    72. public void onError(Throwable error, Session session) {
    73. System.err.println("WebSocket错误:" + error.getMessage());
    74. sessionSet.remove(session);
    75. }
    76. }

    这个示例中定义了一个WebSocket端点ChatWebSocketEndpoint,使用@ServerEndpoint("/websocket")注解来指定WebSocket访问的路径。在onOpen方法中,当有新的客户端连接时,将其Session对象添加到一个静态的sessionSet中,并向客户端发送连接成功的消息。在onMessage方法中,根据收到的消息内容判断是群聊还是私聊,并调用相应的方法来发送消息。sendGroupMessage方法用于发送群聊消息,遍历所有的连接Session对象,并发送消息到每个会话。sendPrivateMessage方法用于发送私聊消息,找到私聊的接收者的Session对象,发送消息给接收者。如果找不到接收者,向发送者发送一条错误消息。

    九.文字和图片消息的处理

     当我们发送的信息包含图片时,可以通过格式进行判断,假如是base64编码的图片,就像下面这样先进行判断,然后在发送给对应的客户端

    1. @OnMessage
    2. public void onMessage(String message, Session session) {
    3. if (message.startsWith("data:image")) { // 判断是否为图片数据
    4. // 处理图片数据
    5. byte[] imageData = getImageDataFromMessage(message);
    6. sendImage(session, imageData);
    7. } else {
    8. // 处理字符串数据
    9. sendText(session, message);
    10. }
    11. }
    12. private byte[] getImageDataFromMessage(String message) {
    13. // 提取图片数据,具体实现略
    14. // 这里假设直接从Base64编码中提取图片数据
    15. String base64Data = message.substring(message.indexOf(",") + 1);
    16. return javax.xml.bind.DatatypeConverter.parseBase64Binary(base64Data);
    17. }
    18. private void sendImage(Session sender, byte[] imageData) {
    19. for (Session session : sessionSet) {
    20. if (session != sender) {
    21. try {
    22. session.getBasicRemote().sendBinary(ByteBuffer.wrap(imageData));
    23. } catch (IOException e) {
    24. e.printStackTrace();
    25. }
    26. }
    27. }
    28. }
    29. private void sendText(Session session, String message) {
    30. try {
    31. session.getBasicRemote().sendText(message);
    32. } catch (IOException e) {
    33. e.printStackTrace();
    34. }
    35. }

  • 相关阅读:
    Layui弹出层关闭后页面自动刷新的用法以及建议
    Seata之AT模式原理详解(三)
    windows操作系统通过浏览器调用本地程序
    C++继承和派生
    学完基础的verilog语言后如何进一步学习fpga
    JVM虚拟机知识点(保姆级教程)
    Taro+react问题集合
    【机器学习】数据均衡学习笔记
    Servlet---请求的分发处理、HttpServlet实现程序、IDEA创建Servlet程序、Servlet继承体系
    零基础学习使用SSRS
  • 原文地址:https://blog.csdn.net/jjhnb123/article/details/134594600