SocketJS學習總結

關於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);
        }
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章