SpringBoot整合WebSocket

引言

本文将介绍前后端引入WebSocket的整个流程。

WebSocket 的实现分为客户端和服务端两部分,客户端(通常为浏览器)发出 WebSocket 连接请求,服务端响应,实现类似 TCP 握手的动作,从而在浏览器客户端和 WebSocket 服务端之间形成一条 HTTP 长连接快速通道。两者之间后续进行直接的数据互相传送,不再需要发起连接和相应。

一、引入WebSocket依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

二、WebSocketConfig

@Configuration
public class WebSocketConfig extends ServerEndpointConfig.Configurator {

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

三、RedisUtils

public List<Object> getForList(String key) {
    //获取两个人信箱里所有的信息
    return redisTemplate.opsForList().range(key, 0, -1);
}

public void setForList(String key, Object value) {
    //向正在发送信息的任意两人的信箱中中添加信息
    redisTemplate.opsForList().rightPush(key, value);
}

四、WebSocketServer

@Data
@NoArgsConstructor
@AllArgsConstructor
public class MessageEntity  implements Serializable {
    // 发送者的 id
    private Long from;
    // 接受者的 id
    private Long to;
    // 具体信息
    private String message;
    // 发送时间
    private Date time;
}

@onMessage 注解的 Java 方法用于接收传入的 WebSocket 信息,这个信息可以是文本格式,也可以是二进制格式。

@OnOpen 在这个端点一个新的连接建立时被调用。参数提供了连接的另一端的更多细节。Session 表明两个 WebSocket 端点对话连接的另一端,可以理解为类似 HTTPSession 的概念。

@OnClose 在连接被终止时调用。参数 closeReason 可封装更多细节,如为什么一个 WebSocket 连接关闭。 

@ServerEndpoint(
        value = "/websocket/{uId}",
        configurator = WebSocketConfig.class)
@Component
public class WebSocketServer {
    private static final Map<Long,Session> onlineUsers = new ConcurrentHashMap<Long, Session>();
    private Session session;
    private static RedisUtils redisUtils;

    @Autowired
    public void setRedisUtils(RedisUtils redisUtils) {
        WebSocketServer.redisUtils = redisUtils;
    }

    @OnOpen
    public void onOpen(@PathParam("uId")String uId, Session session){
        this.session = session;
        onlineUsers.put(Long.valueOf(uId),session);
    }

    @OnMessage
    public void onMessage(String s) throws IOException {
        if(s.equals("ping")) {
            return;
        }
        MessageEntity message = JSON.parseObject(s, MessageEntity.class);
        // 将信息存储到 redis 中
        String key = LongStream.of(message.getFrom(), message.getTo())
                .sorted()
                .mapToObj(String::valueOf)
                .collect(Collectors.joining("-"));
        redisUtils.setForList(key, message);
        if(!message.getFrom().equals(message.getTo())) {
            Session onsession = onlineUsers.get(message.getTo());
            // 如果用户在线就将信息发送给指定用户
            if (onsession != null) {
                onsession.getBasicRemote().sendText(JSON.toJSONString(message));
            }
        }
    }

    @OnClose
    public void onClose(Session session){
        for (Map.Entry<Long, Session> entry : onlineUsers.entrySet()) {
            if (this.session.getId().equals(entry.getValue().getId())) {
                onlineUsers.remove(entry.getKey());
                return;
            }
        }
    }

    @OnError
    public void onError(Throwable error) {
        error.printStackTrace();
    }
}

五、定义接口获取两个人的信息

@PostMapping("/pullMsg")
public ResponseResult pullMsg(@RequestParam("from") Long from, @RequestParam("to") Long to){
        // 根据两人的 id 生成键,并到 redis 中获取
        String key = LongStream.of(from, to)
                .sorted()
                .mapToObj(String::valueOf)
                .collect(Collectors.joining("-"));
        return ResponseResult.ok(redisUtils.getForList(key));
}

六、前端WebSocket

WebSocketServer 连接成功后,会触发 onopen 消息;如果连接失败,发送、接收数据失败或者处理数据出现错误, 会触发 onerror 消息;当 接收到 WebSocketServer 发送过来的数据时,就会触发 onmessage 消息,参数 event 中包含 Server 传输过来的数据;当 接收到 WebSocketServer 端发送的关闭连接请求时,就会触发 onclose 消息。

data() {
    return {
      msg: '',
      socket: null,
	  user: {},
      msgList: []
    }
},
methods: {
	createWebSocket(){
		// 渲染界面时, 根据用户的 id 获取 websocket 连接
		this.socket = new WebSocket(`ws://127.0.0.1:7878/websocket/${this.user.id}`)
		this.socket.onopen = this.websocketopen;
		this.socket.onerror = this.websocketonerror;
		this.socket.onmessage = this.websocketmessage
		this.socket.onclose = this.websocketclose;
		window.onbeforeunload = () => {
		  this.socket.close();
		};
		this.$router.afterEach(() =>{
		  this.socket.close()
		})
	},
    sendMsg() {
	  if(this.msg == '' || this.msg.trim() == ''){
		  return this.$message.warning("发送内容不能为空!")
	  }
      let entity = {
        from: this.user.id,
        to: this.contact.id,
        message: this.msg,
        time: new Date()
      }
      this.socket.send(JSON.stringify(entity))
      this.msgList.push(entity)
      this.msg = ''
    },
	websocketonerror(e){
		console.log("WebSocket连接发生错误" + e); 
		reconnect()
	},
	websocketopen(){
		console.log("websocket打开")
	},
	websocketclose(e){
		this.socket.close()
		console.log("websocket关闭")
	},
	websocketmessage(event){
		this.msgList.push(JSON.parse(event.data))
	},
}
  • 8
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小码夫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值