springboot整合websocket高級版


上一章節我們說了websocket的優缺點,我們通過websocket和http的對比分析,總結出用websocket的場景。今天小編帶大家通過一個案例使用下升級版的websocket。

sockjs介紹

So長江J算是一個瀏覽器庫,它提供了一個跨瀏覽器的api , 他在瀏覽器和服務端建立了一個低延遲、全雙工、跨域通信通道。

產生的原因

向ie這些瀏覽器可能缺少對websocket的支持,我們上一章節也是在谷歌瀏覽器下開發完成的。這里對ie這些瀏覽器沒有做測試,但是一些低版本的瀏覽器的確是不支持的websocket的。
sockJs對瀏覽器兼容性很大。在原聲的websocket基礎上進行了優化。sockjs在不支持websocket的瀏覽器上會采用輪詢的方式實現雙向通信。

環境搭建

springboot整合sockjs

springboot 對sockjs支持性很良好。只需要在原有的websocket配置上添加已sockjs方式發布就可以了。


@Configuration
//注解開啟使用STOMP協議來傳輸基於代理(message broker)的消息,這時控制器支持使用@MessageMapping,就像使用@RequestMapping一樣
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer   {


    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {//注冊STOMP協議的節點(endpoint),並映射指定的url
        //注冊一個STOMP的endpoint,並指定使用SockJS協議
        registry.addEndpoint("/endpointAric")
                .setAllowedOrigins("*")
                .addInterceptors(createSessionInterceptor())
                .withSockJS();

    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {//配置消息代理(Message Broker)
        //廣播式應配置一個/topic消息代理
        registry.enableSimpleBroker("/topic", "/queue");
        //registry.setApplicationDestinationPrefixes("/app");
        //點對點使用的訂閱前綴(客戶端訂閱路徑上會體現出來),不設置的話,默認也是/user/
        registry.setUserDestinationPrefix("/user/");

    }

    /**
     * 配置客戶端入站通道攔截器
     */
    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        registration.setInterceptors(createUserInterceptor());

    }


    @Bean
    public HandshakeInterceptor createSessionInterceptor(){
        return new SessionAuthHandshakeInterceptor();
    }
    /*將客戶端渠道攔截器加入spring ioc容器*/
    @Bean
    public UserInterceptor createUserInterceptor() {
        return new UserInterceptor();
    }


    @Override
    public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
        registration.setMessageSizeLimit(500 * 1024 * 1024);
        registration.setSendBufferSizeLimit(1024 * 1024 * 1024);
        registration.setSendTimeLimit(200000);
    }
}

registry.enableSimpleBroker("/topic", "/queue");設置的topic,queue就是客戶端和服務端的通信通道。

使用場景

本節內容我們將通過聊天室體驗下websocket的使用。

聊天室開發

上面我們已經配置了服務端的環境,這樣我們就可以開發通道內的通信內容。聊天室使用websocket進行通信解決了我們通過ajax調用的實時性。保證了消息的准確性。無延后性。

點對點通信


/*點對點通信*/
@MessageMapping(value = "/sendToUser")
public void templateTest1(@Payload  String message, @Header("userId") String userId,
                          @Headers Map<String, Object> headers) {
    int i = 1;
    for (SimpUser user : userRegistry.getUsers()) {
        System.out.println("用戶" + i++ + "---" + user);
    }
    CustomUser userInfo = (CustomUser) ((Map)(headers.get("simpSessionAttributes"))).get(CoreConstants.USERINFO);
    String fromUserId = String.valueOf(userInfo.getUserId());
    //發送消息給指定用戶
    messagingTemplate.convertAndSendToUser(userId, "/queue/message",new AricResponse(fromUserId,userId,message));
    if (!fromUserId.equals(userId)) {
        //給自己發送一條,消息同步
        messagingTemplate.convertAndSendToUser(fromUserId, "/queue/message",new AricResponse(fromUserId,userId,message));
    }
    //消息新增
    messageService.insertBackMessage(fromUserId,userId,message);
}

@MessageMapping(value = "sendToUser") 該注解實現了接受客戶端的請求。對應客戶端可以通過
stompClient.send("/sendToUser", {'userId': userId},content);進行發送至服務端,userId是客戶端用戶發送給指定用戶的id, content是發送的內容
服務端通過@Payload和@Header注解接受前端傳送的信息。上述代碼中獲取發送者用戶id的地方為什么那么寫呢。下面我們看看客戶端連接的方式


// 建立連接對象(還未發起連接)
    socket = new SockJS(host+"/endpointAric");

    // 獲取 STOMP 子協議的客戶端對象
    stompClient = Stomp.over(socket);

    // 向服務器發起websocket連接並發送CONNECT幀
    stompClient.connect(
        {
            userId: currentUser.userId // 攜帶客戶端信息
        },
        function connectCallback(frame) {
            // 連接成功時(服務器響應 CONNECTED 幀)的回調方法
            subscribe();
            console.log("連接成功");
        },
        function errorCallBack(error) {
            // 連接失敗時(服務器響應 ERROR 幀)的回調方法
            console.log("連接失敗"+error);
            if (errorTimes < 10) {
                errorTimes++;
                setTimeout("connect();",8000);
            }
        }
    );

在連接的時候客戶端會將當前用戶的id傳遞進來,這里也解釋了為什么服務端那樣獲取用戶信息。然后就通過/queue/message發送給指定的用戶。因為我們在配置websocket的時候指定了
registry.setUserDestinationPrefix("/user/");,所以服務端發送給/queue/message了,客戶端訂閱的時候需要加上user 即 /user/queue/message


function subscribe() {
    stompClient.subscribe('/user/queue/message', function (response) {
        var returnData = JSON.parse(response.body);
        if (returnData.fromUserId == returnData.toUserId) {
            //自己發送的消息需要自己渲染到聊天框中
            setMessageInnerHTML(currentUser.userId, returnData, 0);
        } else if (returnData.fromUserId == currentUser.userId) {
            //自己發送信息給別,自己收到的信息
            setMessageInnerHTML(returnData.toUserId, returnData, 1);
        } else {
            //別人發送的信息
            setMessageInnerHTML(returnData.fromUserId, returnData, 0);
        }
    });
}

群聊


/**
 * 多房間聊天室
 * @param chatRoomId
 * @param message
 * @return
 */
@MessageMapping("/welcome/{chatRoomId}") //當瀏覽器向服務端發送請求時,通過@MessageMapping映射/welcome這個地址,類似於@ResponseMapping
@SendTo("/topic/getResponse/{chatRoomId}")//當服務器有消息時,會對訂閱了@SendTo中的路徑的瀏覽器發送消息
public AricResponse say(@Header("userId") String userId,@DestinationVariable("chatRoomId") String chatRoomId, AricMessage message) {

    try {
        //睡眠1秒
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return new AricResponse(userId,null,"welcome," + message.getName()+chatRoomId + "!");
}

群聊相對與單聊簡單很多,我們發送信息的時候只需要發送到房間通道內就行。而在次群聊內的人只需要訂閱房號的信息就行了。這里代碼不在贅述。有疑問的小伙伴可以通過下方加入戰隊找到我。

效果

私聊界面

總結

sockjs在websocket基礎上進行各個瀏覽器的兼容,讓我們的開發變得友好起來。
如果你使用Java做服務端,同時又恰好使用Spring Framework作為框架,那么推薦使用SockJS,因為Spring Framework本身就是SockJS推薦的Java Server實現,同時也提供了Java 的client實現。

如果你使用Node.js做服務端,那么毫無疑問你該選擇Socket.IO,它本省就是從Node.js開始的,當然服務端也提供了engine.io-server-java實現。甚至你可以使用

加入戰隊

微信公眾號

微信公眾號
封面


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM