Java backend + frontend usa WebSocket para realizar el envío de mensajes


Durante el desarrollo del proyecto, me encontré con la necesidad de realizar la función del servidor que envía activamente datos a la página de inicio. Darse cuenta de esta función no es más que usar la tecnología de sondeo y websocket, pero después de considerar el tiempo real y el consumo de recursos, finalmente decidí usar websocket. Ahora registremos la implementación de la tecnología Websocket en Java~
    Por lo general, hay dos formas de implementar Websocket en Java: 1. Crear una clase WebSocketServer, que contiene métodos como abrir, cerrar, mensaje, error, etc.; 2. Usar webSocketHandler class proporcionada por Springboot para crear su clase secundaria y anular el método. Aunque nuestro proyecto usa el marco Springboot, todavía adopta el primer método.

Cree un proceso de operación de ejemplo simple de WebSocket

1. Introducir la dependencia de Websocket

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-websocket -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
            <version>2.7.0</version>
        </dependency>

2. Cree una clase de configuración WebSocketConfig

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

/**
 * 开启WebSocket支持
 */
@Configuration
public class WebSocketConfig {
    
    
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
    
    
        return new ServerEndpointExporter();
    }
}

3. Crear WebSocketServer

Bajo el protocolo websocket, el servidor backend es equivalente al cliente en ws, debe usar @ServerEndpoint para especificar la ruta de acceso y usar @Component para inyectar en el contenedor

@ServerEndpoint: cuando la clase ServerEndpointExporter se declara y usa a través de la configuración de Spring, buscará clases anotadas con @ServerEndpoint. La clase anotada se registrará como punto final de WebSocket. Todos los elementos de configuración están en los atributos de esta anotación
(como: @ServerEndpoint("/ws") )

En las siguientes castañas, @ServerEndpoint especifica que la ruta de acceso contiene sid, que se usa para distinguir cada página


import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.net.Socket;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

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

    //记录连接的客户端
    public static Map<String, Session> clients = new ConcurrentHashMap<>();

    /**
     * userId关联sid(解决同一用户id,在多个web端连接的问题)
     */
    public static Map<String, Set<String>> conns = new ConcurrentHashMap<>();

    private String sid = null;

    private String userId;


    /**
     * 连接成功后调用的方法
     * @param session
     * @param userId
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
    
    
        this.sid = UUID.randomUUID().toString();
        this.userId = userId;
        clients.put(this.sid, session);

        Set<String> clientSet = conns.get(userId);
        if (clientSet==null){
    
    
            clientSet = new HashSet<>();
            conns.put(userId,clientSet);
        }
        clientSet.add(this.sid);
        log.info(this.sid + "连接开启!");
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
    
    
        log.info(this.sid + "连接断开!");
        clients.remove(this.sid);
    }

    /**
     * 判断是否连接的方法
     * @return
     */
    public static boolean isServerClose() {
    
    
        if (NoticeWebsocket.clients.values().size() == 0) {
    
    
            log.info("已断开");
            return true;
        }else {
    
    
            log.info("已连接");
            return false;
        }
    }

    /**
     * 发送给所有用户
     * @param noticeType
     */
    public static void sendMessage(String noticeType){
    
    
        NoticeWebsocketResp noticeWebsocketResp = new NoticeWebsocketResp();
        noticeWebsocketResp.setNoticeType(noticeType);
        sendMessage(noticeWebsocketResp);
    }


    /**
     * 发送给所有用户
     * @param noticeWebsocketResp
     */
    public static void sendMessage(NoticeWebsocketResp noticeWebsocketResp){
    
    
        String message = JSONObject.toJSONString(noticeWebsocketResp);
        for (Session session1 : NoticeWebsocket.clients.values()) {
    
    
            try {
    
    
                session1.getBasicRemote().sendText(message);
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }
    }

    /**
     * 根据用户id发送给某一个用户
     * **/
    public static void sendMessageByUserId(String userId, NoticeWebsocketResp noticeWebsocketResp) {
    
    
        if (!StringUtils.isEmpty(userId)) {
    
    
            String message = JSONObject.toJSONString(noticeWebsocketResp);
            Set<String> clientSet = conns.get(userId);
            if (clientSet != null) {
    
    
                Iterator<String> iterator = clientSet.iterator();
                while (iterator.hasNext()) {
    
    
                    String sid = iterator.next();
                    Session session = clients.get(sid);
                    if (session != null) {
    
    
                        try {
    
    
                            session.getBasicRemote().sendText(message);
                        } catch (IOException e) {
    
    
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    /**
     * 收到客户端消息后调用的方法
     * @param message
     * @param session
     */
    @OnMessage
    public void onMessage(String message, Session session) {
    
    
        log.info("收到来自窗口"+this.userId+"的信息:"+message);
    }

    /**
     * 发生错误时的回调函数
     * @param error
     */
    @OnError
    public void onError(Throwable error) {
    
    
        log.info("错误");
        error.printStackTrace();
    }

}

Un objeto que encapsula un mensaje de envío se puede usar directamente

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

@Data
@ApiModel("ws通知返回对象")
public class NoticeWebsocketResp<T> {
    
    

    @ApiModelProperty(value = "通知类型")
    private String noticeType;

    @ApiModelProperty(value = "通知内容")
    private T noticeInfo;

}

4. llamada websocket

Un usuario llama a la interfaz, envía activamente la información al backend y el backend la envía activamente a los usuarios especificados o a todos después de recibirla.


@RestController
@RequestMapping("/order")
public class OrderController {
    
    
	@GetMapping("/test")
    public R test() {
    
    
    	NoticeWebsocket.sendMessage("你好,WebSocket");
        return R.ok();
    }
}

Conexión WebSocket frontal

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>SseEmitter</title>
</head>
<body>
<div id="message"></div>
</body>
<script>
var limitConnect = 0;
init();
function init() {
      
      
var ws = new WebSocket('ws://192.168.2.88:9060/notice/1');
// 获取连接状态
console.log('ws连接状态:' + ws.readyState);
//监听是否连接成功
ws.onopen = function () {
      
      
    console.log('ws连接状态:' + ws.readyState);
    limitConnect = 0;
    //连接成功则发送一个数据
    ws.send('我们建立连接啦');
}
// 接听服务器发回的信息并处理展示
ws.onmessage = function (data) {
      
      
    console.log('接收到来自服务器的消息:');
    console.log(data);
    //完成通信后关闭WebSocket连接
    // ws.close();
}
// 监听连接关闭事件
ws.onclose = function () {
      
      
    // 监听整个过程中websocket的状态
    console.log('ws连接状态:' + ws.readyState);
reconnect();

}
// 监听并处理error事件
ws.onerror = function (error) {
      
      
    console.log(error);
}
}
function reconnect() {
      
      
    limitConnect ++;
    console.log("重连第" + limitConnect + "次");
    setTimeout(function(){
      
      
        init();
    },2000);
   
}
</script>
</html>

Una vez que se inicia el proyecto, la consola imprime la información de conexión después de abrir la página.
inserte la descripción de la imagen aquí
Después de llamar al método de pedido/prueba, el front-end imprime el contenido del mensaje push.
inserte la descripción de la imagen aquí
De esta manera, la comunicación websocket se puede realizar llamando al URL a través de la interfaz o ws ~
Si no hay una página de inicio, también puede usar la prueba WebSocket en línea
inserte la descripción de la imagen aquí

¡Está bien, sal de clase! ! !

Supongo que te gusta

Origin blog.csdn.net/poker_zero/article/details/126184697
Recomendado
Clasificación