
1. 即時通訊簡述
即時通訊是端開發作業中常見的需求,本篇文章以作者作業中使用FLutter開發社交軟體即時通訊需求為背景,描述一下即時通訊功能設計的要點,
2. 重要概念
即時通訊需要前后端配合,約定訊息格式與訊息內容,本次IM客戶端需求開發使用了公司已有的基于Socket.io搭建的后臺,下文描述涉及到的一些概念,
2.1 WebSocket協議
WebSocket是一種在單個TCP連接上進行全雙工通信的協議,WebSocket協議與傳統的HTTP協議的主要區別為,WebSocket協議允許服務端主動向客戶端推送資料,而傳統的HTTP協議服務器只有在客戶端主動請求之后才能向客戶端發送資料,在沒有WebSocket之前,即時通訊大部分采用長輪詢方式,
2.2 Socket.io和WebSocket的區別
Socket.io不是WebSocket,它只是將WebSocket和輪詢 (Polling)機制以及其它的實時通信方式封裝成了通用的介面,并且在服務端實作了這些實時機制的相應代碼,也就是說,WebSocket僅僅是Socket.io實作即時通信的一個子集,因此WebSocket客戶端連接不上Socket.io服務端,當然Socket.io客戶端也連接不上WebSocket服務端,
2.3 服務端socket訊息
理解了服務端socket訊息也就理解了服務器端的即時通訊邏輯,服務器發出的socket訊息可以分為兩種:
-
服務器主動發出的訊息:
例如,社交軟體中的A用戶給B用戶發出了訊息,服務器在收到A用戶的訊息后,通過socket鏈接,將A用戶的訊息轉發給B用戶,B用戶客戶端接收到的訊息就屬于服務器主動發出的,其他比較常見的場景例如直播軟體中,全平臺用戶都會收到的禮物訊息廣播,
-
服務器在接收到客戶端訊息后的回傳訊息:
例如,長鏈接心跳機制,客戶端向服務器發送ping訊息,服務器在成功接受客戶端的ping訊息后回傳的pong訊息就屬于服務器的回傳訊息,其他常見的場景如社交軟體中A用戶給B用戶發出了訊息,服務器在收到A用戶的訊息后,給A客戶端回傳一條訊息,供A客戶端了解訊息的發送狀態,判斷發送是否成功,大部分場景,服務器在接收到客戶端主動發出的訊息之后都需要回傳一條訊息,
3. 客戶端實作流程
幾個設計客戶端即時通訊的重點,
3.1 心跳機制
所謂心跳就是客戶端發出ping訊息,服務器成功收到后回傳pong訊息,當客戶端一段時間內不在發送ping訊息,視為客戶端斷開,服務器就會主動關閉socket鏈接,當客戶端發送ping訊息,服務器一段時間內沒有回傳pong訊息,視為服務器斷開,客戶端就會啟動重連機制,

3.2 重連機制
重連機制為客戶端重新發起連接,常見的重連條件如下:
- 客戶端發送ping訊息,服務器一段時間內沒有回傳pong,
- 客戶端網路斷開,
- 服務器主動斷開連接,
- 客戶端主動連接失敗,
當出現極端情況(客戶端斷網)時,頻繁的重連可能會導致資源的浪費,可以設定一段時間內的最大重連次數,當重連超過一定次數時,休眠一段時間,
3.3 訊息發送流程
- 將訊息存盤到本地資料庫,發送狀態設為等待,
- 發送socket訊息,
- 接收到服務器回傳的socket訊息后,將本地資料庫等待狀態的訊息改為成功,
注意事項:
將訊息存盤到本地資料庫時需要生成一個id存入資料庫,同時傳給服務器,當收到訊息時根據id判斷更新本地資料庫的哪一條訊息,
3.4 訊息接收流程

3.5 其他相關
- 聊天頁訊息的排序:在查詢本地資料庫時使用
order by按時間排序, - 訊息串列:也推薦做本地存盤,當收到訊息的時候需要先判斷本地訊息串列是否有當前訊息用戶的對話框,如果沒有就先插入,有就更新,訊息串列的維護就不展開說了,感興趣可以看代碼,
- 圖片語音訊息:將圖片和語言先上傳到專門的服務器上(各種專門的云存盤服務器),sokcet訊息和本地存盤傳遞的是云服務器上的URL,
- 多人聊天(群聊):與單人聊天邏輯基本一致,區別位本地資料庫需要添加一個會話ID欄位,打開一個群就查詢對應會話ID的資料,聊天訊息不再是誰發給誰,而是在哪個群聊下,
4. 客戶端Flutter代碼
把部分代碼貼上來,完整專案在作者的github上,
4.1 心跳機制
heart() {
pingTimer = Timer.periodic(Duration(seconds: 30), (data) {
if (pingWaitTime >= 60) {
socket.connect();
pingWaitTime = 0;
pingWaitTimer!.cancel();
ping();
}
if (!pingWaitFlag) ping();
});
}
ping() {
debugPrint("ping");
String pingData =
'https://www.cnblogs.com/jackbwublog/archive/2021/10/15/{"type":"ping","payload":{"front":true},"msg_id":${DateTime.now().millisecondsSinceEpoch}}';
socket.emit("message", pingData);
pingWaitFlag = true;
pingWaitTime = 0;
pingWaitTimer = Timer.periodic(Duration(seconds: 1), (data) {
pingWaitTime++;
print(data.hashCode);
if (pingWaitTime % 10 == 0) debugPrint(pingWaitTime.toString());
});
}
//pong
if (socketMessage.type == PONG && socketMessage.code == 1000) {
pingWaitFlag = false;
pingWaitTimer!.cancel();
pingWaitTime = 0;
}
4.2 本地資料庫設計
資料庫表的設計是比較重要的,理解了資料庫設計,讀代碼也就無壓力了,
//訊息表
CREATE TABLE chatDetail (
chat_id TEXT PRIMARY KEY,//主鍵
from_id TEXT,//發送人
to_id TEXT,//接收人
created_at TEXT,
content TEXT,//訊息內容
image TEXT,//UI展示用,用戶頭像
name TEXT,//UI展示用,用戶名
sex TEXT,//UI展示用,用戶性別
status TEXT,//訊息狀態
type INTEGER,//訊息型別,圖片/文字/語音等
chat_object_id TEXT//聊天物件ID,對當前用戶而言的聊天物件,是一系列本地操作的核心
)
//訊息串列表
CREATE TABLE chatList (
cov_id TEXT,
unread_count INTEGER,
last_msg_text TEXT,
last_msg_at TEXT,
image TEXT,
name TEXT,
sex TEXT,
chat_object_id TEXT PRIMARY KEY)
5. 總結
無論是Flutter技術,或是IOS/Android/Web,只要掌握了即時通訊的核心開發流程,不同的技術只是API有些變化,API往往看檔案就能解決,大前端或是特定平臺的工程師還是要掌握核心開發流程,會幾種做同樣事情的API意義不大,
demo寫的比較簡單,有問題可以評論,
專案github地址,

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/319613.html
標籤:其他
