Escenas
WebSocket
El protocolo HTTP es un protocolo de capa de aplicación sin estado, sin conexión y unidireccional. Utiliza un modelo de solicitud / respuesta. La solicitud de comunicación solo puede ser iniciada por el cliente y el servidor responde a la solicitud.
Este modelo de comunicación tiene un inconveniente: el protocolo HTTP no puede permitir que el servidor inicie activamente mensajes al cliente.
La característica de esta solicitud unidireccional está destinada a ser muy problemático para que el cliente sepa si el servidor tiene cambios de estado continuos. La mayoría de las aplicaciones web implementarán un sondeo largo a través de frecuentes solicitudes asincrónicas de JavaScript y XML (AJAX). El sondeo es ineficiente y desperdicia recursos (porque la conexión debe mantenerse conectada o la conexión HTTP siempre está abierta).
Así es como se inventó WebSocket. La conexión WebSocket permite la comunicación full-duplex entre el cliente y el servidor para que cualquiera de las partes pueda enviar datos al otro extremo a través de la conexión establecida. WebSocket solo necesita establecer una conexión una vez y puede permanecer conectado todo el tiempo. En comparación con el establecimiento continuo de la conexión en el modo de sondeo, obviamente la eficiencia mejora enormemente.
Si sigue la versión separada de front-end y back-end para enseñarle a configurar el entorno localmente y ejecutar el proyecto:
https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/108465662
Después de configurar los proyectos anteriores y posteriores. Realice la integración de WebSocket de SpringBoot y Vue en segundo plano.
Nota:
Blog:
https://blog.csdn.net/badao_liumang_qizhi
Siga la
cuenta pública
Programadores dominantes Obtenga libros electrónicos relacionados con la programación, instructivos y descargas gratuitas.
lograr
Integración SpringBoot
Primero introduzca las dependencias en el archivo pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
Luego cree una nueva clase de configuración para WebSocket para habilitar la compatibilidad con WebSocket
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
Luego cree una nueva clase de entidad cliente WebSocketClient para almacenar la sesión y la Uri conectadas
import javax.websocket.Session;
public class WebSocketClient {
// 与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
//连接的uri
private String uri;
public Session getSession() {
return session;
}
public void setSession(Session session) {
this.session = session;
}
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
}
Luego cree un nuevo WebSocketService para crear y procesar conexiones
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
@ServerEndpoint(value = "/websocket/{userName}")
@Component
public class WebSocketService {
private static final Logger log = LoggerFactory.getLogger(WebSocketService.class);
//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static int onlineCount = 0;
//concurrent包的线程安全Set,用来存放每个客户端对应的WebSocketServer对象。
private static ConcurrentHashMap<String, WebSocketClient> webSocketMap = new ConcurrentHashMap<>();
/**与某个客户端的连接会话,需要通过它来给客户端发送数据*/
private Session session;
/**接收userName*/
private String userName="";
/**
* 连接建立成功调用的方法*/
@OnOpen
public void onOpen(Session session, @PathParam("userName") String userName) {
if(!webSocketMap.containsKey(userName))
{
addOnlineCount(); // 在线数 +1
}
this.session = session;
this.userName= userName;
WebSocketClient client = new WebSocketClient();
client.setSession(session);
client.setUri(session.getRequestURI().toString());
webSocketMap.put(userName, client);
log.info("----------------------------------------------------------------------------");
log.info("用户连接:"+userName+",当前在线人数为:" + getOnlineCount());
try {
sendMessage("来自后台的反馈:连接成功");
} catch (IOException e) {
log.error("用户:"+userName+",网络异常!!!!!!");
}
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
if(webSocketMap.containsKey(userName)){
webSocketMap.remove(userName);
if(webSocketMap.size()>0)
{
//从set中删除
subOnlineCount();
}
}
log.info("----------------------------------------------------------------------------");
log.info(userName+"用户退出,当前在线人数为:" + getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息*/
@OnMessage
public void onMessage(String message, Session session) {
log.info("收到用户消息:"+userName+",报文:"+message);
//可以群发消息
//消息保存到数据库、redis
if(StringUtils.isNotBlank(message)){
}
}
/**
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
log.error("用户错误:"+this.userName+",原因:"+error.getMessage());
error.printStackTrace();
}
/**
* 连接服务器成功后主动推送
*/
public void sendMessage(String message) throws IOException {
synchronized (session){
this.session.getBasicRemote().sendText(message);
}
}
/**
* 向指定客户端发送消息
* @param userName
* @param message
*/
public static void sendMessage(String userName,String message){
try {
WebSocketClient webSocketClient = webSocketMap.get(userName);
if(webSocketClient!=null){
webSocketClient.getSession().getBasicRemote().sendText(message);
}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocketService.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocketService.onlineCount--;
}
public static void setOnlineCount(int onlineCount) {
WebSocketService.onlineCount = onlineCount;
}
public static ConcurrentHashMap<String, WebSocketClient> getWebSocketMap() {
return webSocketMap;
}
public static void setWebSocketMap(ConcurrentHashMap<String, WebSocketClient> webSocketMap) {
WebSocketService.webSocketMap = webSocketMap;
}
public Session getSession() {
return session;
}
public void setSession(Session session) {
this.session = session;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
Tenga en cuenta que el WebSocketClient al que se hace referencia aquí es la clase de entidad creada anteriormente para almacenar información relacionada con la conexión.
然后 @ServerEndpoint (valor = "/ websocket / {userName}")
Aquí está la dirección de la conexión WebSocket, el siguiente {userName} se usa para recibir los parámetros pasados por el front-end, usados como un identificador diferente, y luego hacer un procesamiento diferente
Luego, el siguiente método anotado de OnOpen es el método que se llamará después de que la conexión se establezca correctamente, aquí está el proceso de conteo del número de usuarios en línea, puede hacerlo de acuerdo con el suyo
El negocio necesita ser resuelto.
Luego, OnClose anota el método llamado por la relación de conexión.
Luego, OnMessage anota el método de devolución de llamada cuando el cliente envía un mensaje, que procesa los datos de acuerdo con sus necesidades.
El método sendMessage utiliza la sesión actual para enviar un mensaje de retroalimentación al cliente cuando el cliente se conecta al servidor.
Luego, envíe un mensaje al cliente especificado mediante el método sendMessage, que pasa el identificador único del usuario y el contenido del mensaje.
Cree una nueva interfaz de controlador para probar el envío de mensajes
import com.ruoyi.web.websocket.WebSocketService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/websocket")
public class WebSocketController {
@GetMapping("/pushone")
public void pushone()
{
WebSocketService.sendMessage("badao","公众号:霸道的程序猿");
}
}
Si se trata de un proyecto SpringBoot normal, el backend está bien. Entonces, debido a que se usa el marco creado por Ruoyi anteriormente, la URL de ws y la URL de la interfaz deben liberarse para la autenticación de autorización.
Integración del proyecto Vue
Cree un nuevo componente WebSocket, que se coloca en el directorio de componentes.
Luego declara algunas variables
data() {
return {
// ws是否启动
wsIsRun: false,
// 定义ws对象
webSocket: null,
// ws请求链接(类似于ws后台地址)
ws: '',
// ws定时器
wsTimer: null,
}
},
Ejecute el método de inicialización de la conexión websocket en la función montada
async mounted() {
this.wsIsRun = true
this.wsInit()
},
En la implementación del método de inicialización
const wsuri = 'ws://你的后台ip:7777/websocket/badao'
this.ws = wsuri
if (!this.wsIsRun) return
// 销毁ws
this.wsDestroy()
// 初始化ws
this.webSocket = new WebSocket(this.ws)
// ws连接建立时触发
this.webSocket.addEventListener('open', this.wsOpenHanler)
// ws服务端给客户端推送消息
this.webSocket.addEventListener('message', this.wsMessageHanler)
// ws通信发生错误时触发
this.webSocket.addEventListener('error', this.wsErrorHanler)
// ws关闭时触发
this.webSocket.addEventListener('close', this.wsCloseHanler)
// 检查ws连接状态,readyState值为0表示尚未连接,1表示建立连接,2正在关闭连接,3已经关闭或无法打开
clearInterval(this.wsTimer)
this.wsTimer = setInterval(() => {
if (this.webSocket.readyState === 1) {
clearInterval(this.wsTimer)
} else {
console.log('ws建立连接失败')
this.wsInit()
}
}, 3000)
},
Realice una conexión websocket y pase los parámetros de badao, y configure el método de procesamiento de devolución de llamada correspondiente.
Finalmente, se configura un temporizador de 3 segundos para verificar periódicamente el estado de la conexión del websocket.
Este componente también agrega un botón y establece su evento de clic para enviar mensajes al servidor
sendDataToServer() {
if (this.webSocket.readyState === 1) {
this.webSocket.send('来自前端的数据')
} else {
throw Error('服务未连接')
}
},
Código completo del componente de WebSocket
<template>
<el-button @click="sendDataToServer" >给后台发送消息</el-button>
</template>
<script>
export default {
name: "WebSocket",
data() {
return {
// ws是否启动
wsIsRun: false,
// 定义ws对象
webSocket: null,
// ws请求链接(类似于ws后台地址)
ws: '',
// ws定时器
wsTimer: null,
}
},
async mounted() {
this.wsIsRun = true
this.wsInit()
},
methods: {
sendDataToServer() {
if (this.webSocket.readyState === 1) {
this.webSocket.send('来自前端的数据')
} else {
throw Error('服务未连接')
}
},
/**
* 初始化ws
*/
wsInit() {
const wsuri = 'ws://10.229.36.158:7777/websocket/badao'
this.ws = wsuri
if (!this.wsIsRun) return
// 销毁ws
this.wsDestroy()
// 初始化ws
this.webSocket = new WebSocket(this.ws)
// ws连接建立时触发
this.webSocket.addEventListener('open', this.wsOpenHanler)
// ws服务端给客户端推送消息
this.webSocket.addEventListener('message', this.wsMessageHanler)
// ws通信发生错误时触发
this.webSocket.addEventListener('error', this.wsErrorHanler)
// ws关闭时触发
this.webSocket.addEventListener('close', this.wsCloseHanler)
// 检查ws连接状态,readyState值为0表示尚未连接,1表示建立连接,2正在关闭连接,3已经关闭或无法打开
clearInterval(this.wsTimer)
this.wsTimer = setInterval(() => {
if (this.webSocket.readyState === 1) {
clearInterval(this.wsTimer)
} else {
console.log('ws建立连接失败')
this.wsInit()
}
}, 3000)
},
wsOpenHanler(event) {
console.log('ws建立连接成功')
},
wsMessageHanler(e) {
console.log('wsMessageHanler')
console.log(e)
//const redata = JSON.parse(e.data)
//console.log(redata)
},
/**
* ws通信发生错误
*/
wsErrorHanler(event) {
console.log(event, '通信发生错误')
this.wsInit()
},
/**
* ws关闭
*/
wsCloseHanler(event) {
console.log(event, 'ws关闭')
this.wsInit()
},
/**
* 销毁ws
*/
wsDestroy() {
if (this.webSocket !== null) {
this.webSocket.removeEventListener('open', this.wsOpenHanler)
this.webSocket.removeEventListener('message', this.wsMessageHanler)
this.webSocket.removeEventListener('error', this.wsErrorHanler)
this.webSocket.removeEventListener('close', this.wsCloseHanler)
this.webSocket.close()
this.webSocket = null
clearInterval(this.wsTimer)
}
},
}
}
</script>
<style scoped>
</style>
Luego, haga referencia al componente en la página de inicio
<template>
<div class="app-container home">
<el-row :gutter="20">
websocket推送
<WebSocket></WebSocket>
</el-row>
<el-divider />
</div>
</template>
import WebSocket from '@/components/WebSocket/WebSocket'
export default {
name: "index",
components: {
WebSocket
},
efecto
Primero ejecute el servicio SpringBoot en segundo plano, luego ejecute el proyecto de front-end e inicie sesión en la página de inicio, abra la salida de la consola
Puede ver que la conexión websocket se estableció correctamente y que hay una salida en segundo plano.
Luego, el fondo empuja el mensaje al front-end y llama a la interfaz para enviar el mensaje en segundo plano.
Entonces puede recibir el impulso desde el fondo en la parte frontal
Luego haga clic en el botón en la parte frontal para enviar un mensaje al fondo
El mensaje enviado se recibe en el método de devolución de llamada en segundo plano.