一.什么是WebSocket
- WebSocket是基于TCP协议的一种网络协议,它实现了浏览器与服务器全双工通信,支持客户端和服务端之间相互发送信息。在有WebSocket之前,如果服务端数据发生了改变,客户端想知道的话,只能采用定时轮询的方式去服务端获取,这种方式很大程度上增大了服务器端的压力,有了WebSocket之后,如果服务端数据发生改变,可以立即通知客户端,客户端就不用轮询去换取,降低了服务器的压力。目前主流的浏览器都已经支持WebSocket协议了。
二.SpringBoot整合
- pom依赖
<!--webSocket--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> <version>${web-socket.version}</version> </dependency> 版本:2.1.17.RELEASE
- 配置文件
//自动注册WebSocket endpoint 启动类上也需要加@EnableWebSocket @Configuration public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } }
- server端
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.huawen.common.constant.Constants; import com.huawen.common.core.redis.RedisCache; import lombok.extern.slf4j.Slf4j; 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; /** * @author:kyrie * @date:2022/9/16 10:40 * @Description: websocket 服务端 **/ @Component @ServerEndpoint("/websocket/{userId}") @Slf4j public class WebSocketServer { /** * 存放每个客户端对应的WebSocket对象。 */ private static final ConcurrentHashMap<Long, WebSocketServer> WEB_SOCKET_USER_MAP = new ConcurrentHashMap<>(); /** * 与某个客户端的连接会话,需要通过它来给客户端发送数据 */ private Session session; /** * 接收userId */ private Long userId; /** * 空的成功的json */ private static final JSONObject EMPTY_SUCCESS_JSON = JSONUtil.createObj() .put("code", "SUCCESS") .put("message", "操作成功") .put("result", ""); /** * redis tool */ private RedisCache redisCache; /** * 连接建立成功调用的方法 * * @param session session会话 只有知道session才可以知道发给谁 * @param userId 登录人的id */ @OnOpen public void onOpen(Session session, @PathParam("userId") Long userId) { this.session = session; this.userId = userId; this.redisCache = SpringUtil.getBean(RedisCache.class); WEB_SOCKET_USER_MAP.remove(userId); WEB_SOCKET_USER_MAP.put(userId, this); log.info("用户连接:--->{}", userId); sendMessage(EMPTY_SUCCESS_JSON); } /** * 关闭方法 */ @OnClose public void onClose() { WEB_SOCKET_USER_MAP.remove(userId); log.info("用户退出:--->{}", userId); } /** * 出现异常 * * @param error 异常 */ @OnError public void onError(Throwable error) { log.error("用户错误:--->{},原因:--->{}", this.userId, error.getMessage()); } /** * 收到客户端消后调用的方法 * * @param message 客户端发送过来的消息 **/ @OnMessage public void onMessage(String message) { log.info("用户消息:--->{}报文:--->{}", userId, message); if (StrUtil.isBlank(message)) { return; } // 消息保存到redis try { // 解析发送的报文 JSONObject jsonObject = JSONUtil.parseObj(message); // 处理自己的业务逻辑 } } catch (Exception e) { log.error("onMessage error--->{}", e.getMessage()); } } /** * 发送消息给某人 * * @param jsonObject {@link JSONObject} json * @param toUserId 接收人id */ public static void sendInfo(JSONObject jsonObject, Long toUserId) { log.info("发送消息到:--->{},报文:--->{}", toUserId, jsonObject.toString()); if (ObjectUtil.isNotNull(toUserId) && WEB_SOCKET_USER_MAP.containsKey(toUserId)) { WEB_SOCKET_USER_MAP.get(toUserId).sendMessage(jsonObject); } else { log.error("用户--->{},不在线!", toUserId); } } /** * 发送消息 json类型 * * @param jsonObject {@link JSONObject } JSON对象 */ public void sendMessage(JSONObject jsonObject) { try { this.session.getBasicRemote().sendText(JSONUtil.toJsonStr(jsonObject)); } catch (IOException e) { log.error("sendMessage error--->{}", e.getMessage()); } } }
- 测试发送消息
package com.huawen.websocket; import cn.hutool.json.JSONObject; import com.huawen.common.constant.Constants; import com.huawen.common.core.controller.BaseController; import com.huawen.common.core.domain.protocols.APIResult; import com.huawen.common.core.redis.RedisCache; import com.huawen.common.utils.SecurityUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.TimeUnit; /** * @author:kyrie * @date:2022/9/16 10:56 * @Description: 测试webSocket controller **/ @RestController @RequestMapping("/socket") public class WebSocketController extends BaseController { @Autowired private RedisCache redisCache; @GetMapping("/send") public APIResult<String> sendMessage() { Long loginUserId = SecurityUtils.getLoginUserId(); JSONObject object = new JSONObject(); object.put(Constants.DATA_STR, loginUserId); object.put(Constants.TYPE_STR, Constants.MESSAGE_STR); // TODO: 2022/9/16 根据实际人员变化 object.put(Constants.COUNT_STR, 2); WebSocketServer.sendInfo(object, loginUserId); redisCache.setCacheObject(Constants.WEB_SOCKET_PREFIX + loginUserId, object.toString(), Constants.REDIS_TIMEOUT, TimeUnit.SECONDS); return success(); } }