SpringBoot集成websocket

SpringBoot集成websocket

  • 说明
  • 依赖
  • 项目结构
  • WebSocketServer 代码
  • WebSocketConfig代码
  • Controller测试代码
  • 页面测试

说明

这里因为公司有个项目需要使用kafka生产和消费消息,然后我这里要将消费到的kafka消息主动推送给前端,所以这里使用了websocket向客户端主动推送消息。
对应我的这篇文章:SpringBoot集成Kafka低版本和高版本,这个里面我没有写websocket相关的代码,这里额外单独写一篇说明。

依赖

fastjson版本自己随便选择,websocket这里选择的是1.4.2.RELEASE版本,因为项目的SpringBoot版本依赖是1.4.2.RELEASE。

		<!--json对象与字符串互转-->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.83</version>
		</dependency>
		<!--集成websocket,与springboot版本相对应-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-websocket</artifactId>
			<version>1.4.2.RELEASE</version>
		</dependency>

项目结构

只需要配置这2个,websocket即可集成成功。
SpringBoot集成websocket_第1张图片

WebSocketServer 代码

这里我是将uuid作为唯一进行连接,这里的uuid为前端生成

package com.gmcc.project.controllers.websocket;

import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

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

/**
 * webSocket服务端
 * 客户端本地测试可以使用这个网站:https://www.dute.org/websocket
 */
@ServerEndpoint("/webSocket/{uuid}")
@Component
public class WebSocketServer {
    private final Logger logger = LoggerFactory.getLogger(WebSocketServer.class);
    /**
     * 以uuid为key,连接会话为对象保存起来
     */
    public static Map<String, Session> websocketClients = new ConcurrentHashMap<>();
    /**
     * 会话
     */
    private Session session;
    /**
     * 根据uuid唯一标识建立websocket通道
     */
    private String uuid;

    /**
     * 发送消息到指定连接
     * @param uuid uuid唯一标识
     * @param jsonString 消息
     */
    public static void sendMessage(String uuid,String jsonString){
        Session nowSession = websocketClients.get(uuid);
        if(nowSession!=null){
            try {
                nowSession.getBasicRemote().sendText(jsonString);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //建立连接成功调用
    @OnOpen
    public void onOpen(@PathParam("uuid") String uuid, Session session)
    {
        this.uuid = uuid;
        this.session = session;
        if(websocketClients.get(uuid)==null){
            websocketClients.put(uuid, session);
            System.out.println("当前uuid:"+uuid+"已创建连接");
        }
    }

    //错误时调用
    @OnError
    public void onError(Session session, Throwable error) {
        logger.info("服务端发生了错误"+error.getMessage());
    }

    /**
     * 连接关闭
     */
    @OnClose
    public void onClose()
    {
        websocketClients.remove(uuid);
        System.out.println("当前uuid:"+uuid+"已退出连接");
    }

    /**
     * 接收客户端的消息
     *
     * @param message 消息
     * @param session 会话
     */
    @OnMessage
    public void onMessage(String message, Session session){
        System.out.println("服务端从客户端uuid为"+uuid+"收到了一条消息:"+message);
        //向客户端回传消息
        //session.getAsyncRemote().sendText("来自服务器:"+uuid+"你的消息我收到啦");
    };

    /**
     * 向所有连接主动推送消息
     * @param jsonObject 消息体
     * @throws IOException
     */
    public static void sendMessageAll(JSONObject jsonObject) throws IOException {
        for (Session item : websocketClients.values()) {
            item.getAsyncRemote().sendText(jsonObject.toJSONString());
        }
    }
}


WebSocketConfig代码

package com.gmcc.project.controllers.config;

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

/**
 * WebScoket配置处理器
 */
@Configuration
public class WebSocketConfig {
    /**
     * ServerEndpointExporter 作用
     *
     * 这个Bean会自动注册使用@ServerEndpoint注解声明的websocket endpoint
     *
     * @return
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

Controller测试代码

package com.gmcc.project.controllers.kafka;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.gmcc.project.controllers.websocket.WebSocketServer;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

//kafka生产者
@RestController
@RequestMapping("kafkaProducer")
public class KafkaProducerController {

    @RequestMapping(value = "/message", method = RequestMethod.POST)
    public void message(@RequestParam(value = "uuid", required = false) String uuid,
                     @RequestParam(value = "message", required = false) String message) {
        try {
            //向webSocket客户端里面的指定uuid推送消息
            WebSocketServer.sendMessage(uuid,message);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @RequestMapping(value = "/messageAll", method = RequestMethod.POST)
    public void messageAll(@RequestParam(value = "message", required = false) String message) {
        try {
            //向所有连接主动推送消息
            String msg="{\"code\": \"200\", \"message\": \""+message+"\",\"result\": \""+message+"\"}";
            JSONObject jsonObject= JSON.parseObject(msg);
            WebSocketServer.sendMessageAll(jsonObject);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

页面测试

后端服务启动后,这里使用这个网站:https://www.dute.org/websocket,模拟前端连接后端服务。这里我项目的端口是17890,如果你的项目端口是8080的话,改为8080就可以了。
注意:websocket地址格式,如果是http请求,那就是ws开头。如果是https开头,那就是wss开头才能连接上。
websocket中http格式:ws://ip:项目端口号/项目名称(得看你的项目有没有配置,没有可不写)/@ServerEndpoint注解里面的路径值
SpringBoot集成websocket_第2张图片
然后点击连接按钮,连接成功
SpringBoot集成websocket_第3张图片

后端日志打印:
SpringBoot集成websocket_第4张图片
这里我打开三个页面进行模拟连接,如下:
第一个:
SpringBoot集成websocket_第5张图片
第二个,这里的第二个我的uuid写的和第一个一样,这里是故意写的,原因后面会说:
SpringBoot集成websocket_第6张图片
第三个:
SpringBoot集成websocket_第7张图片
连接成功后,我这里使用postman进行测试:
向指定uuid发送消息
SpringBoot集成websocket_第8张图片
然后看页面结果:
这是第一个
SpringBoot集成websocket_第9张图片
第二个,结果如下:
发现没有接收到消息,这里之所以这样是代码里面使用了ConcurrentHashMap,只会对第一个连接的有效,后面相同的uuid连接就不会收到消息,这里之所以这样做,是基于这样考虑的,如果你使用的是用户名进行登录的,然后这个用户名又可以同时登录多台电脑,然后这时候假设有一个人使用了这个用户名进行了websocket操作,其他人也登录了这个用户,但是没有进行websocket操作,这时候是不是就是只有那个进行了websocket操作的才能接收到后端返回的消息,如果其他登录的没有进行websocket操作的用户也收到了消息,是不是就会不合理。
这里还是得看个人功能需求。需求不一样,功能实现也就不一样。
SpringBoot集成websocket_第10张图片
第三个,结果如下:
结果正常,没有消息,因为是往uuid为111的发送消息,所以uuid为222的理当收不到消息
SpringBoot集成websocket_第11张图片
向所有连接主动推送消息:
SpringBoot集成websocket_第12张图片
结果如下:
第一个正常收到消息
SpringBoot集成websocket_第13张图片
第二个:
没有收到消息,因为第一个已经有了
SpringBoot集成websocket_第14张图片
第三个:
正常收到消息
SpringBoot集成websocket_第15张图片
好了,我的websocket集成demo到这里就分享完成了。

你可能感兴趣的:(SpringBoot篇,spring,boot,websocket,java,后端,kafka)