53. Springboot supports websocket in two ways-------1. Develop WebSocket based on annotations to simply implement a multi-person chat interface

Develop WebSocket based on annotations

–The annotations are: @OnOpen, @OnClose, @OnMessage, @OnError, etc.

★ Two development methods of WebSocket

▲ Spring Boot provides two development methods for WebSocket:

  • Developing WebSocket based on spring-boot-starter-websocket.jar

  • Develop WebSocket based on Spring WebFlux

Comparison of two methods:
springboot API
Insert image description here

Socket: socket.
socket.
Establish virtual Sockets (sockets) at both ends of the communication, and the network protocol will be responsible for establishing a virtual line between the two Sockets. Then the two ends of the communication can carry out real-time and reliable communication through the virtual line.

WebSocket is a Socket that establishes two-way communication between the server and the client browser. This ensures real-time two-way communication between the server and the client browser.

▲ Traditional Web applications: request-response. This model does not allow the server to actively push data to the client's browser. There are also such as SSE (server event mechanism)

★ Develop WebSocket based on Spring Boot automatic configuration

two steps:

(1) Define a WebSocket processing class

该处理类有两种开发方式(历史原因造成):

第一种方式:   - 直接使用JDK提供的 WebSocket注解 修饰处理方法,
                并使用 @ServerEndpoint  注解修饰该处理类即可。
                WebSocket注解就是: @OnOpen、 @OnClose 、 @OnMessage 、@OnError这些
  ——这种方式下方法签名可以随便写,方法参数也可以自行定义,非常灵活。
  官方也推荐尽量使用第一种方式

第二种方式:    - 实现WebSocketHandler接口、并实现该接口中定义的各种处理方法。
  ——Java语法规定,实现接口时,实现的方法必须与接口中定义的方法有相同的方法签名。
  ——这种方式就不够灵活。

(2) Configure or export the WebSocket processing class

如果采用注解方式开发WebSocket处理类,
这一步只需要在 Spring容器 中配置一个ServerEndpointExporter Bean  (导出器)即可。
该导出器会负责将容器中所有的@ServerEndpoint注解修饰的处理Bean都导出成WebSocket。
因此无论程序有多少个WebSocket处理Bean,导出器Bean只要配置一个即可。
——非常简单、方便。

如果采用是实现接口的方式开发WebSocket处理类,
这一步就需要使用WebSocketConfigurer来配置WebSocket。

code demo

create project
Insert image description here

Two steps to develop websocket.
Insert image description here

Annotation: @PathParam()

Belongs to WebSocket and is used to obtain parameters on the path.
Insert image description here

Simple explanation for sending a message:
Insert image description here

Complete code:

front page
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title> 基于WebSocket的多人聊天 </title>
	<script type="text/javascript">
		// 定义Web Socket对象
		var webSocket = null;
		let sendMsg = function()
		{
    
    
			if (webSocket == null || webSocket.readyState != 1)
			{
    
    
				document.getElementById('show').innerHTML
					+= "还未连接服务器,请先连接WebSocket服务器<br>";
				return;
			}
			let inputElement = document.getElementById('msg');
			// 发送消息
			webSocket.send(inputElement.value);
			// 清空单行文本框
			inputElement.value = "";
		}
		let connect = function()
		{
    
    
			let name = document.getElementById('name').value.trim();
			if (name == null || name == "")
			{
    
    
				document.getElementById('show').innerHTML
					+= "用户名不能为空<br>";
				return;
			}
			if (webSocket && webSocket.readyState == 1)
			{
    
    
				webSocket.close();
			}
			webSocket = new WebSocket("ws://127.0.0.1:8080/websocket/" + name);
			webSocket.onopen = function()
			{
    
    
				document.getElementById('show').innerHTML
					+= "恭喜您,连接服务器成功!<br>";
				document.getElementById('name').value = "";
				// 为onmessage事件绑定监听器,接收消息
				webSocket.onmessage= function(event)
				{
    
    
					// 接收、并显示消息
					document.getElementById('show').innerHTML
						+= event.data + "<br>";
				}
			};
		}
	</script>
</head>
<body>
<input type="text" size="20" id="name" name="name"/>
<input type="button" value="连接" onclick="connect();"/>
<div style="width:600px;height:240px;
	overflow-y:auto;border:1px solid #333;" id="show"></div>
<input type="text" size="80" id="msg" name="msg"/>
<input type="button" value="发送" onclick="sendMsg();"/>
</body>
</html>
MyWebSocketHandler annotation development methods
package cn.ljh.app.websocket;

import lombok.SneakyThrows;
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.Map;
import java.util.concurrent.ConcurrentHashMap;


//基于注解开发 WebSocket --注解就是: @OnOpen、 @OnClose 、 @OnMessage 、@OnError这些

@Component
//这个注解需要指定指定映射地址
@ServerEndpoint("/websocket/{name}")
@Slf4j
public class MyWebSocketHandler
{
    
    
    //可以用注解来修饰不同的处理方法

    //假设是一个聊天室,map 中的key 是每个用户的 Session , value 是用户的name
    //创建一个线程安全的map ,用这个map来保存各用户的Session与name之间的对应关系
    public final static Map<Session,String> myClients = new ConcurrentHashMap<>();

    //当浏览器 与 Websocket 服务器建立连接的时候触发该方法
    //这个Session参数代表浏览器 与 WebSocket 服务器所建立连接会话,它用于通信。
    //这个 @PathParam() 相当于spring mvc 中的 @PathVariable,
    //都是用来获取路径中的参数--获取这个路径"/websocket/{name}" 中的name参数

    @OnOpen
    public void onOpen(Session session , @PathParam("name") String name)
    {
    
    
        //当用户登录的时候,把信息存进去
        myClients.put(session,name);
        log.debug("--------- onOpen ----------");
    }

    //当浏览器与WebSocket服务器 关闭的时候触发该方法
    @OnClose
    public void onClose(Session session)
    {
    
    
        //当连接关闭的时候,就将该客户端从map中删除
        myClients.remove(session);
        log.debug("--------- onClose ----------");
    }


    //当 WebSocket服务器 收到 浏览器 发送过来的消息时触发该方法
    @SneakyThrows
    @OnMessage
    public String onMessage(Session session, String message)
    {
    
    
        //收到消息,把消息广播给每个客户端      keySet()-->将map中的所有key存到set集合中
        for (Session client : myClients.keySet())
        {
    
    
            //此处的 client 就代表每个客户端
            //向client 发送信息,这个 client 代表了一个浏览器
            //getBasicRemote() 表示给客户端发送消息是同步的、阻塞的    ,   sendText()  发送消息
            client.getBasicRemote().sendText(myClients.get(session)+" 说:"+message);

        }
        log.debug("--------- onMessage ----------"+message);
        return null;
    }

    //但 WebSocket 服务器 与 浏览器通信出现异常时触发该方法
    //这个 Throwable 参数就代表了出现的异常
    @OnError
    public void onError(Session session, Throwable ex)
    {
    
    
        //当连接出现异常的时候,将该客户端从 Map 中栅除
        myClients.remove(session);
        log.debug("--------- onError ----------");
    }


}
MyWebSocketConfig configures the WebSocket exporter
package cn.ljh.app.config;

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

/**
 * author JH
 * date 2023/9/7  0007 12:28
 */

@Configuration
public class MyWebSocketConfig
{
    
    
    /*
     *配置 WebSocket 导出器
     *
     *   该导出器会负责将容器中所有的 @ServerEndpoint 注解修饰的 处理Bean 都导出成 WebSocket。
     *  因此无论程序有多少个 WebSocket 处理Bean,导出器Bean 只要配置一个即可。
     */

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

application.yml configuration log level
#给这个包设置日志级别为debug
logging:
  level:
    cn:
      ljh:
        app:
          websocket: debug

test:

Make a websocket connection
Insert image description here

Successfully developed a WebSocket multi-person chat room based on annotations
Insert image description here

Guess you like

Origin blog.csdn.net/weixin_44411039/article/details/132686847