三分鐘帶你實現一個微信聊天室,快和心中想念的人聊個天websocket高級版|Java 開發實戰

這是我參與更文挑戰的第3天,活動詳情查看: 更文挑戰前端

本文正在參加「Java主題月 - Java 開發實戰」,詳情查看 活動連接java

[TOC]web

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

sockjs介紹

SockJs算是一個瀏覽器庫,它提供了一個跨瀏覽器的api , 他在瀏覽器和服務端創建了一個低延遲、全雙工、跨域通訊通道。spring

產生的緣由

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

環境搭建

springboot整合sockjs

springboot 對sockjs支持性很良好。只須要在原有的websocket配置上添加已sockjs方式發佈就能夠了。和以前websocket章節同樣咱們須要添加對sockjs的配置。只須要withSockJS就能夠開啓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的使用。springboot

聊天室開發

上面咱們已經配置了服務端的環境,這樣咱們就能夠開發通道內的通訊內容。聊天室使用websocket進行通訊解決了咱們經過ajax調用的實時性。保證了消息的準確性。無延後性。服務器

sockJs的好處就是被springboot封裝一層以後!咱們開發websocket通道就像咱們開發接口同樣方便。咱們只須要經過messageMapping 註解就能夠完成咱們數據的接收。剩下的客戶端的工做仍是以前的一套工做邏輯只須要響應的使用sockjs就能夠進行通訊了。

點對點通訊

/*點對點通訊*/
@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實現。甚至你可使用

相關文章
相關標籤/搜索