关于WebSocket的介绍和相关API,网上有很多文档,感觉下面这个说得比较细:

  1. WebSocket介绍
  2. WebSocket API
  3. SockJS Fallback
  4. WebSocket STOMP

这个是SpringBoot整合 WebSocket的文档,前端用的SocketJS/Stomp。后端用的是Spring所支持SocketJS:springboot websocket 一篇足够了

前端用的Stomp来获取client进行SocketJS的API操作:Stomp Over Websocket文档

我们知道SocketJS对Cookie的兼容性不好,网上有看到可以把Cookie信息放到Header中进行传输及获取,这是相关的整合文档:
Spring+WebSocket+SockJS实现实时聊天

关于为什么不直接用原生的WebSocket而用SocketJS应该不用我多说,就是IE10以下不支持ws协议,为了它的兼容性,需要在不支持websocket浏览器下降级成长轮询的方式。而SocketJS和SocketIO都封装了WebSocket。而SocketIO后端官方是用NodeJS实现的,如果要用Java当服务器端,可以使用基于Netty实现的netty-socketio:githup地址,它官方也提供了相应的demo,提出了namespace/room的概念,比较适合当聊天工具的开发,只是要开启新的端口来监听连接(socketJS就不需要)

补充一下:SocketJS中有个registry.setUserDestinationPrefix("/user");有点不容易被理解,看了源码,在convertAndSendToUser和convertAndSend方法时体现了他们的不同之处

SimpMessagingTemplate.class

// convertAndSendToUser方法底层调用的convertAndSend方法,只不过参数添加了config中的destinationPrefix及点对点的接收人
super.convertAndSend(this.destinationPrefix + user + destination, payload, headers, postProcessor);

另附上后端调用api时主要的代码及注释:

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.annotation.SendToUser;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.socket.WebSocketSession;
import top.fissile.manager.SocketManager;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.security.Principal;
import java.util.HashMap;
import java.util.Map;

/**
 * demo地址:http://123.56.157.29:3333/html/index.html
 * @Author duln
 * @Date 2019/12/27 11:27
 * @Version 1.0
 */
@RestController
@Slf4j
public class TestController {
    @Autowired
    private SimpMessagingTemplate template;

    /**
     * @Description 接收客户端的消息
     * @param principal 这是在拦截器中设置的数据,之前只用token给设置个name值
     * @param msg 客户端发送过来的消息(也可以用map接收,前端就需要obj转json)
     */
    // 只和前端的发送参数相关,类似RequestMapping,前端直接clent.send('/sendAllUser',...)就可以
    @MessageMapping("/sendAllUser")
    // 貌似得和@MessageMapping连用才生效,也就限制了一般只能用在controller中
    // 区别于@SendToUser,@SendTo是用来服务器给客户端群发的:value就是群发的destination,方法返回值就是群发数据
    // 如果返回值为Map,则客户端收到的是application/json格式;如果返回值是String,则收到的是xxx/text格式
    // @SendTo同template.convertAndSend(),只是template可用于任何地方
    @SendTo("/topic/receiveMsg")
    public Map<String, Object> sendAllUser(Principal principal, String msg) {
        Map<String, Object> map = new HashMap<>();
        map.put("type", "公开");
        map.put("userName", principal.getName());
        map.put("message", msg);
        return map;
    }

    /**
     * 客户端请求获取当前人数信息
     */
    @MessageMapping("/getCount")
    public void getCount(Principal principal) {
        Map<String, Object> map = SocketManager.get();
        // 与convertAndSend不同的是:this.destinationPrefix + user
        // super.convertAndSend(this.destinationPrefix + user + destination, payload, headers, postProcessor);
        template.convertAndSendToUser(principal.getName(), "/queue/count", map);
    }

    /**
     * 点对点用户聊天
     */
    @MessageMapping("/sendOneUser")
    public void sendOneUser(Principal principal, Map<String, String> map) {
        log.info("map = {}", map);
        String toName = map.get("userName");
        String fromName = principal.getName();
        WebSocketSession webSocketSession = SocketManager.get(toName);
        if (webSocketSession != null) {
            Map<String, Object> toMap = new HashMap<>();
            toMap.put("type", "私聊");
            toMap.put("message", map.get("message"));
            Map<String, Object> fromMap = new HashMap<>(toMap);
            toMap.put("fromName", fromName);
            toMap.put("toName", "你");

            fromMap.put("fromName", "你");
            fromMap.put("toName", toName);
            template.convertAndSendToUser(toName, "/queue/sendUser", toMap);
            template.convertAndSendToUser(fromName, "/queue/sendUser", fromMap);
        }
    }

}
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐