Springboot 实现WebSocket通信

Springboot 实现WebSocket通信

引入WebSocket所需要starter模块依赖包;pom.xml文件引入依赖

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
 		<dependency>
 			<groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.75</version>
        </dependency>

编写WebSocketConfig配置类

package com.gremlin.webSocket;

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

/**
 * @className: WebSocketConfig
 * @author: gremlin
 * @version: 1.0.0
 * @description: 引入自动配置类这个配置类ServerEndpointExporter,主要用于扫描WebSocket相关的注解
 * @date: 2023/03/08 14:00
 */
@Configuration
@EnableWebSocket
public class WebSocketConfig {
    
    

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

编写发送对象数据的编码器

package com.gremlin.webSocket;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.json.JsonMapper;
import org.apache.poi.ss.formula.functions.T;

import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;

/**
 * 发送object对象的编码器
 * @author admin
 */
public class ServerEncoder implements Encoder.Text<Page<T>> {
    
    
 
    @Override
    public void destroy() {
    
     }
 
    @Override
    public void init(EndpointConfig arg0) {
    
     }

    @Override
    public String encode(Page<T> tPage) throws EncodeException {
    
    
        try {
    
    
            /*
             * 这里是重点,只需要返回Object序列化后的json字符串就行
             * 你也可以使用gosn,fastJson来序列化。
             */
            JsonMapper jsonMapper = new JsonMapper();
            return jsonMapper.writeValueAsString(tPage);

        } catch ( JsonProcessingException e) {
    
    
            e.printStackTrace();
            return null;
        }
    }
}

定义一个websocket类,注册到Spring容器中,使用@ServerEndpoint注解把当前类定义成一个服务器端并标记好客户端发起WebSocket连接请求的URL,暴露ws应用的路径

package com.gremlin.webSocket;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.gremlin.service.NewsService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.websocket.EncodeException;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @className: WebSocket
 * @author: gremlin
 * @version: 1.0.0
 * @description: 注解@ServerEndpoint 暴露的ws应用的路径
 * @date: 2023/03/08 14:03
 */
@Component
@Slf4j
@ServerEndpoint(value = "/websocket/{userId}", encoders = {
    
    ServerEncoder.class})
public class WebSocket {
    
    

    /**
     * spring默认是单例的,而WebSocket是多对象的,也就是每次会产生不同的对象。
     * 在初始化项目的时候,WebSocket就会产生一个对象,这时候就会注入service了,而当客户端与WebSocket服务端连接过后,
     * 又会产生一个新对象,而spring默认是单例,只会给一个相同的对象注入一次service,
     * 因此这时候的WebSocket新对象就不会再注入service了,
     * 再去调用该service中的方法的话也就会发生空指针异常。
     * 因此要写出静态 构造注入 注入的时候,给类的 service 注入
     */
    private static NewsService newsService;

    @Autowired
    public void setNewsService (NewsService newsService) {
    
    
        WebSocket.newsService= newsService;
    }

    /**
     * 用来存储服务连接对象
     */
    private static Map<String,WebSocket> clientMap = new ConcurrentHashMap<>();

	/**
	 * @Open:当WebSocket连接建立成功后会触发这个注解修饰的方法;
	 * @Close:当WebSocket连接关闭或中断后会触发这个注解修饰的方法;
	 * @OnMessage:当客户端发送消息到服务端时,会触发这个注解修饰的方法;
	 * @OnError:当 websocket 建立连接时出现异常会触发这个注解修饰的方法;
	 */
    private Session session;
    private String userId;

    /**
     * 客户端与服务端连接成功
     */
    @OnOpen
    public void onOpen(Session session,@PathParam("userId") String userId) throws IOException {
    
    
        this.session = session;
        this.userId = userId;
        // 与当前客户端连接成功时
        if (clientMap.containsKey(userId)){
    
    
            clientMap.remove(userId);
            clientMap.put(userId,this);
        } else{
    
    
            clientMap.put(userId,this);
        }
        // sendAllMessage("连接成功","连接成功");
        // 连接成功则立刻发送内容
        sendInfo("连接成功",userId);
        log.info("-----------------连接成功---------------: " + userId);
    }

    /**
     * 客户端与服务端连接关闭
     */
    @OnClose
    public void onClose(Session session){
    
    
        log.info("连接关闭:"+userId);
        // 与当前客户端连接关闭时
        clientMap.remove(userId);
    }

    /**
     * 客户端向服务端发送消息
     */
    @OnMessage
    public void onMsg(Session session,String message) throws IOException {
    
    
        // 收到来自当前客户端的消息时
        log.info("客户端向服务端发送消息: "+ userId);
        clientMap.get(userId).sendAllMessage(message,"");
    }

    /**
     * 向客户端发送消息
     */
    private void sendAllMessage(String message,Object data) throws IOException {
    
    
        log.info("向客户端发送消息:"+userId);
        try {
    
    
            this.session.getBasicRemote().sendObject(data);
            log.info("服务端向客户端发送消息成功!");
        } catch (IOException | EncodeException e) {
    
    
            e.printStackTrace();
        }
    }

    /**
     * 发送自定义消息
     * */
    public void sendInfo(String message,@PathParam("userId") String userId) throws IOException {
    
    
        log.info("发送消息到: " + userId + ",message: " + message);
        if(StringUtils.isNotBlank(userId) && clientMap.containsKey(userId)){
    
    
            // 此处调用接口获取需要发送的数据,此处的data可以是集合也可以是Page等
            clientMap.get(userId).sendAllMessage(message,data);
        }else{
    
    
            log.error("发送消息错误,接收用户不在线");
        }
    }

    /**
     * 客户端与服务端连接异常
     */
    @OnError
    public void onError(Throwable error,Session session) {
    
    
        log.info("客户端与服务端连接异常:"+userId);
        error.printStackTrace();
    }
}

猜你喜欢

转载自blog.csdn.net/rq12345688/article/details/129406798