在线聊天使用了SpringBoot+WebSocket实现,为了保证用户隐私,所有的聊天数据都保存在系统本地,服务器只进行了数据转发。OK,那接下来,我们来看下大致的实现步骤。

服务端

服务端首先加入websocket依赖,如下:

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-websocket</artifactId>
  4. </dependency>

创建WebSocket的配置类,如下:

  1. @Configuration
  2. @EnableWebSocketMessageBroker
  3. public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
  4. @Override
  5. public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
  6. stompEndpointRegistry.addEndpoint("/ws/endpointChat").withSockJS();
  7. }
  8. @Override
  9. public void configureMessageBroker(MessageBrokerRegistry registry) {
  10. registry.enableSimpleBroker("/queue","/topic");
  11. }
  12. }

这里我并未使用原生的websocket协议,而是使用了websocket的子协议stomp,方便一些。消息代理既使用了/queue,又使用了/topic,主要是因为我这里既有点对点的单聊,也有发送系统消息的群聊。

创建websocket处理类controller,如下:

  1. @Controller
  2. public class WsController {
  3. @Autowired
  4. SimpMessagingTemplate messagingTemplate;
  5. @MessageMapping("/ws/chat")
  6. public void handleChat(Principal principal, String msg) {
  7. String destUser = msg.substring(msg.lastIndexOf(";") + 1, msg.length());
  8. String message = msg.substring(0, msg.lastIndexOf(";"));
  9. messagingTemplate.convertAndSendToUser(destUser, "/queue/chat", new ChatResp(message, principal.getName()));
  10. }
  11. }

消息协议很简单:发送来的消息,最后一个;后面跟的是该条消息要发送到哪个用户,响应消息包含两个字段,一个是消息内容,一个是该条消息由谁发送。

OK,如此之后,我们的服务端就写好了,很简单。

前端

前端代码稍微复杂,我这里主要和小伙伴介绍下我的大致思路和核心代码,具体代码小伙伴可以star源码进行研究。

首先,当用户登录成功之后,我就发起websocket的连接,将ws连接起来,ws的代码我主要写在了store中,如下:

  1. connect(context){
  2. context.state.stomp = Stomp.over(new SockJS("/ws/endpointChat"));
  3. context.state.stomp.connect({}, frame=> {
  4. context.state.stomp.subscribe("/user/queue/chat", message=> {
  5. var msg = JSON.parse(message.body);
  6. var oldMsg = window.localStorage.getItem(context.state.user.username + "#" + msg.from);
  7. if (oldMsg == null) {
  8. oldMsg = [];
  9. oldMsg.push(msg);
  10. window.localStorage.setItem(context.state.user.username + "#" + msg.from, JSON.stringify(oldMsg))
  11. } else {
  12. var oldMsgJson = JSON.parse(oldMsg);
  13. oldMsgJson.push(msg);
  14. window.localStorage.setItem(context.state.user.username + "#" + msg.from, JSON.stringify(oldMsgJson))
  15. }
  16. if (msg.from != context.state.currentFriend.username) {
  17. context.commit("addValue2DotMap", "isDot#" + context.state.user.username + "#" + msg.from);
  18. }
  19. //更新msgList
  20. var oldMsg2 = window.localStorage.getItem(context.state.user.username + "#" + context.state.currentFriend.username);
  21. if (oldMsg2 == null) {
  22. context.commit('updateMsgList', []);
  23. } else {
  24. context.commit('updateMsgList', JSON.parse(oldMsg2));
  25. }
  26. });
  27. }, failedMsg=> {
  28. });
  29. }

连接成功之后,就可以准备接收服务端的消息了,接收到服务端的消息后,数据保存在localStorage中,保存格式是 当前用户名#消息发送方用户名:[{from:'消息发送方',msg:'消息内容'}],注意后面的是一个json数组,这两个人的聊天记录都将保存在这个数组中。在聊天展示页面,当数组中的数据发生变化时,自动更新。

在聊天页面,通过stomp发送消息,如下:

  1. this.$store.state.stomp.send("/ws/chat", {}, this.msg + ";" + this.currentFriend.username);

每次发送成功后更新聊天页面即可。

核心代码基本如此,其他细节小伙伴可以star源码进行研究。