SpringBoot集成websocket
说明
这里因为公司有个项目需要使用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即可集成成功。
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注解里面的路径值
然后点击连接按钮,连接成功
后端日志打印:
这里我打开三个页面进行模拟连接,如下:
第一个:
第二个,这里的第二个我的uuid写的和第一个一样,这里是故意写的,原因后面会说:
第三个:
连接成功后,我这里使用postman进行测试:
向指定uuid发送消息
然后看页面结果:
这是第一个
第二个,结果如下:
发现没有接收到消息,这里之所以这样是代码里面使用了ConcurrentHashMap,只会对第一个连接的有效,后面相同的uuid连接就不会收到消息,这里之所以这样做,是基于这样考虑的,如果你使用的是用户名进行登录的,然后这个用户名又可以同时登录多台电脑,然后这时候假设有一个人使用了这个用户名进行了websocket操作,其他人也登录了这个用户,但是没有进行websocket操作,这时候是不是就是只有那个进行了websocket操作的才能接收到后端返回的消息,如果其他登录的没有进行websocket操作的用户也收到了消息,是不是就会不合理。
这里还是得看个人功能需求。需求不一样,功能实现也就不一样。
第三个,结果如下:
结果正常,没有消息,因为是往uuid为111的发送消息,所以uuid为222的理当收不到消息
向所有连接主动推送消息:
结果如下:
第一个正常收到消息
第二个:
没有收到消息,因为第一个已经有了
第三个:
正常收到消息
好了,我的websocket集成demo到这里就分享完成了。