天天看點

Java實作線上溝通功能1、介紹 和 特點2、整合SpringBoot3、效果4、小結

文章目錄

  • 1、介紹 和 特點
  • 2、整合SpringBoot
    • 2.1、導入依賴
    • 2.2、websocket 配置類
    • 2.3、消息處理類
    • 2.4、啟動服務
    • 2.5、前端代碼:張三
    • 2.6、前端代碼:李四
  • 3、效果
  • 4、小結

1、介紹 和 特點

t-io是基于JVM的網絡程式設計架構,和netty屬同類,是以netty能做的t-io都能做,考慮到t-io是從項目抽象出來的架構,是以t-io提供了更多的和業務相關的API,大體上t-io具有如下特點和能力
  • 内置完備的監控和流控能力
  • 内置半包粘包處理
  • 一騎絕塵的資源管理能力
  • 内置心跳檢查和心跳發送能力
  • 内置IP拉黑
  • 一流性能和穩定性(第三方權威平台TFB提供性能測試和穩定性服務)
  • 極其穩定的表現(很多使用者還是停在t-io 1.x版本,就是因為太過穩定,不想變動)
  • 内置慢攻擊防禦
  • 唯一一個内置異步發送、阻塞發送、同步發送的網絡架構
  • 唯一内置叢集分發消息的能力
  • 獨創的多端口資源共享能力(譬如一個端口是websocket協定,一個端口是私有的im協定,這兩個端口的資源可以共享,這對協定适配極其有用)
  • 獨創協定适配轉換能力(讓基于websocket和基于socket的應用看起來像是同一個協定)
  • 獨一檔的資源和業務綁定能力:綁定group、綁定userid、綁定token、綁定bsId,這些綁定幾乎囊括了所有業務需求

2、整合SpringBoot

2.1、導入依賴

<!-- tio-websocket -->
<dependency>
    <groupId>org.t-io</groupId>
    <artifactId>tio-websocket-server</artifactId>
    <version>3.5.9.v20200214-RELEASE</version>
</dependency>
           

注意:每個版本之前存在差異請檢視官方文檔

2.2、websocket 配置類

import com.asurplus.tio.websocket.handle.MyWsMsgHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.tio.server.ServerTioConfig;
import org.tio.websocket.server.WsServerStarter;
 
import java.io.IOException;
 
/**
 * websocket 配置類
 */
@Configuration
public class WebSocketConfig {
 
   /**
     * 注入消息處理器
     */
    @Autowired
    private MyWsMsgHandler myWsMsgHandler;
 
    /**
     * 啟動類配置
     *
     * @return
     * @throws IOException
     */
    @Bean
    public WsServerStarter wsServerStarter() throws IOException {
        // 設定處理器
        WsServerStarter wsServerStarter = new WsServerStarter(6789, myWsMsgHandler);
        // 擷取到ServerTioConfig
        ServerTioConfig serverTioConfig = wsServerStarter.getServerTioConfig();
        // 設定心跳逾時時間,預設:1000 * 120
        serverTioConfig.setHeartbeatTimeout(1000 * 120);
        // 啟動
        wsServerStarter.start();
        return wsServerStarter;
    }
}
           

這裡我們注入了 WsServerStarter 的 bean,這樣在 SpringBoot 啟動的時候,就能啟動咱們的 websocket 服務

  • 注明了 websocket 的服務端口為:6789
  • 消息處理類為:myWsMsgHandler,在下一步我們将會去實作這個類
  • 設定了心跳的逾時時間為:120秒,預設值,可以不設定

2.3、消息處理類

package com.ying.tiiochat.config;
 
import com.alibaba.fastjson.JSONObject;
import org.springframework.stereotype.Component;
import org.tio.core.ChannelContext;
import org.tio.core.Tio;
import org.tio.http.common.HttpRequest;
import org.tio.http.common.HttpResponse;
import org.tio.websocket.common.WsRequest;
import org.tio.websocket.common.WsResponse;
import org.tio.websocket.server.handler.IWsMsgHandler;
 
/**
 * 消息處理類
 */
@Component
public class MyWsMsgHandler implements IWsMsgHandler {
 
   /**
    * <li>對httpResponse參數進行補充并傳回,如果傳回null表示不想和對方建立連接配接,架構會斷開連接配接,如果傳回非null,架構會把這個對象發送給對方</li>
    * <li>注:請不要在這個方法中向對方發送任何消息,因為這個時候握手還沒完成,發消息會導緻協定互動失敗。</li>
    * <li>對于大部分業務,該方法隻需要一行代碼:return httpResponse;</li>
    *
    * @param httpRequest
    * @param httpResponse
    * @param channelContext
    * @return
    * @throws Exception
    */
   @Override
   public HttpResponse handshake(HttpRequest httpRequest, HttpResponse httpResponse, ChannelContext channelContext) throws Exception {
      // 可以在此做一些業務邏輯,傳回null表示不想連接配接
      return httpResponse;
   }
 
   /**
    * 握手成功後觸發該方法
    *
    * @param httpRequest
    * @param httpResponse
    * @param channelContext
    * @throws Exception
    */
   @Override
   public void onAfterHandshaked(HttpRequest httpRequest, HttpResponse httpResponse, ChannelContext channelContext) throws Exception {
      // 拿到使用者id
      String id = httpRequest.getParam("userId");
      // 綁定使用者
      Tio.bindUser(channelContext, id);
      // 給使用者發送消息
      JSONObject message = new JSONObject();
      message.put("msg", "連接配接成功...");
      message.put("sendName", "系統提醒");
      WsResponse wsResponse = WsResponse.fromText(message.toString(), "UTF-8");
      Tio.sendToUser(channelContext.tioConfig, id, wsResponse);
   }
 
   /**
    * <li>當收到Opcode.BINARY消息時,執行該方法。也就是說如何你的ws是基于BINARY傳輸的,就會走到這個方法</li>
    *
    * @param wsRequest
    * @param bytes
    * @param channelContext
    * @return 可以是WsResponse、byte[]、ByteBuffer、String或null,如果是null,架構不會回消息
    * @throws Exception
    */
   @Override
   public Object onBytes(WsRequest wsRequest, byte[] bytes, ChannelContext channelContext) throws Exception {
      System.out.println("我走了onBytes");
      return null;
   }
 
   /**
    * 當收到Opcode.CLOSE時,執行該方法,業務層在該方法中一般不需要寫什麼邏輯,空着就好
    *
    * @param wsRequest
    * @param bytes
    * @param channelContext
    * @return 可以是WsResponse、byte[]、ByteBuffer、String或null,如果是null,架構不會回消息
    * @throws Exception
    */
   @Override
   public Object onClose(WsRequest wsRequest, byte[] bytes, ChannelContext channelContext) throws Exception {
      // 關閉連接配接
      Tio.remove(channelContext, "WebSocket Close");
      return null;
   }
 
   /**
    * <li>當收到Opcode.TEXT消息時,執行該方法。也就是說如何你的ws是基于TEXT傳輸的,就會走到這個方法</li>
    *
    * @param wsRequest
    * @param text
    * @param channelContext
    * @return 可以是WsResponse、byte[]、ByteBuffer、String或null,如果是null,架構不會回消息
    * @throws Exception
    */
   @Override
   public Object onText(WsRequest wsRequest, String text, ChannelContext channelContext) throws Exception {
      JSONObject message = JSONObject.parseObject(text);
      // 接收消息的使用者ID
      String receiver = message.getString("receiver");
      // 發送消息者
      String sendName = message.getString("sendName");
      // 消息
      String msg = message.getString("msg");
 
      // 儲存聊天記錄到DB等業務邏輯...
 
      WsResponse wsResponse = WsResponse.fromText(message.toString(), "UTF-8");
      Tio.sendToUser(channelContext.tioConfig, receiver, wsResponse);
 
      JSONObject resp = new JSONObject();
      resp.put("sendName", "系統提醒");
      resp.put("msg", "發送成功");
      return resp.toString();
   }
}
           

我們實作了 IWsMsgHandler 接口,并重寫了該接口的 5 個方法,這 5 個方法從 發送握手包,到消息收發,到斷開連接配接等一系列過程

2.4、啟動服務

Java實作線上溝通功能1、介紹 和 特點2、整合SpringBoot3、效果4、小結

啟動成功後,可以看出 tio 的列印結果,我們可以看出服務端口為我們設定的 6789,我們便可以連接配接測試了

2.5、前端代碼:張三

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>websocket通訊</title>
</head>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script type="text/javascript">
    <!--  連接配接-->
    var socket;
    var userName;
    // 連接配接
    function connect() {
        var socketUrl = "ws://localhost:6789/?userId=" + $('#sendName').val();
        if (socket != null) {
            socket.close();
            socket = null;
        }
        socket = new WebSocket(socketUrl);
        //打開事件
        socket.onopen = function () {
            console.log("開始建立連結....")
        };
        //關閉事件
        socket.onclose = function () {
            console.log("websocket已關閉");
        };
        //發生了錯誤事件
        socket.onerror = function () {
            console.log("websocket發生了錯誤");
        };
        /**
         * 接收消息
         * @param msg
         */
        socket.onmessage = function (msg) {
            console.log(msg)
            var json = JSON.parse(msg.data);
            if (msg.msg != '連接配接成功') {
                $("#msgDiv").append('<p class="other" style="color:olivedrab;">' + json.sendName + ':'+json.msg+'</p>');
            }
        };
    }
 
 
    /**
     * 發送消息
     */
    function sendMessage() {
        var msg = $("#msg").val();
        if (msg == '' || msg == null) {
            alert("消息内容不能為空");
            return;
        }
        var receiver = $("#receiver").val();
        if (receiver == '' || receiver == null) {
            alert("接收人不能為空");
            return;
        }
 
        var sendName = $("#sendName").val();
        if (sendName == '' || sendName == null) {
            alert("發送人不能為空");
            return;
        }
        var msgObj = {
            "receiver": receiver,
            "sendName": sendName,
            "msg": msg
        };
         $("#msgDiv").append('<p class="user" style="color: red">' + sendName + ':'+msg+'</p>');
        try{
            socket.send(JSON.stringify(msgObj));
            $("#msg").val('');
        }catch (e) {
            alert("伺服器内部錯誤");
        }
    }
</script>
<body>
使用者名:<input type="text" id="sendName" value="張三">
<input type="button" value="連接配接" onclick="connect()" ><br>
發送者:<input type="text" id="sender" value="張三" ><br>
接受者:<input type="text" id="receiver" value="李四"><br><br>
消  息:<textarea id="msg"></textarea><br><input type="button" value="發送" onclick="sendMessage()"><br><br>
 
消息記錄:<div id="msgDiv" style="border: 1px red solid;width: 400px;height: 200px"></div>
<br>
 
</body>
</html>
           

2.6、前端代碼:李四

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>websocket通訊</title>
</head>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script type="text/javascript">
    <!--  連接配接-->
    var socket;
    var userName;
    // 連接配接
    function connect() {
        var socketUrl = "ws://localhost:6789/?userId=" + $('#sendName').val();
        if (socket != null) {
            socket.close();
            socket = null;
        }
        socket = new WebSocket(socketUrl);
        //打開事件
        socket.onopen = function () {
            console.log("開始建立連結....")
        };
        //關閉事件
        socket.onclose = function () {
            console.log("websocket已關閉");
        };
        //發生了錯誤事件
        socket.onerror = function () {
            console.log("websocket發生了錯誤");
        };
        /**
         * 接收消息
         * @param msg
         */
        socket.onmessage = function (msg) {
            console.log(msg)
            var json = JSON.parse(msg.data);
            if (msg.msg != '連接配接成功') {
                $("#msgDiv").append('<p class="other" style="color:olivedrab;">' + json.sendName + ':'+json.msg+'</p>');
            }
        };
    }
 
 
    /**
     * 發送消息
     */
    function sendMessage() {
        var msg = $("#msg").val();
        if (msg == '' || msg == null) {
            alert("消息内容不能為空");
            return;
        }
        var receiver = $("#receiver").val();
        if (receiver == '' || receiver == null) {
            alert("接收人不能為空");
            return;
        }
 
        var sendName = $("#sendName").val();
        if (sendName == '' || sendName == null) {
            alert("發送人不能為空");
            return;
        }
        var msgObj = {
            "receiver": receiver,
            "sendName": sendName,
            "msg": msg
        };
         $("#msgDiv").append('<p class="user" style="color: red">' + sendName + ':'+msg+'</p>');
        try{
            socket.send(JSON.stringify(msgObj));
            $("#msg").val('');
        }catch (e) {
            alert("伺服器内部錯誤");
        }
    }
</script>
<body>
使用者名:<input type="text" id="sendName" value="李四">
<input type="button" value="連接配接" onclick="connect()" ><br>
發送者:<input type="text" id="sender" value="李四" ><br>
接受者:<input type="text" id="receiver" value="張三"><br><br>
消  息:<textarea id="msg"></textarea><br><input type="button" value="發送" onclick="sendMessage()"><br><br>
 
消息記錄:<div id="msgDiv" style="border: 1px red solid;width: 400px;height: 200px"></div>
<br>
 
</body>
</html>
           

記得有兩個HTML檔案哦

Java實作線上溝通功能1、介紹 和 特點2、整合SpringBoot3、效果4、小結

3、效果

Java實作線上溝通功能1、介紹 和 特點2、整合SpringBoot3、效果4、小結

4、小結

但在實際開發中,遠沒有一對一聊天這麼簡單,我們可能還需要聊天清單,曆史聊天記錄,客戶清單、聊天室、多對多聊天(平台客服、商家客服、使用者),包括 商品快捷方式,訂單資訊快捷方式,不過這些都是可以根據這個Demo的基礎上做出來的。等有時間會根據這個Demo做一個具體的用例

原文:https://blog.csdn.net/weixin_46522803/article/details/126548065?spm=1001.2014.3001.5506