?【Go 實踐】實現一個簡單的多人聊天室
簡介
本文使用 go 實現了一個多人聊天室,參考文章為 Writing a Chat Server in Go,點選檢視中文翻譯版。
本文的特點在於:將原始專案分為自底向上的若干個階段,新手可以一步步地實現系統的不同模組,逐漸掌握相應知識點。原始碼裡有詳細的註釋,引導你在不看原始碼的情況下自己實現相應程式碼。
本文假設你:
- 瞭解 go 語言的基本語法
- 瞭解 git 的使用
完成本專案,你將學會這些內容:
- Reader 方法的基本使用
- 使用
net
包實現一個 tcp 伺服器:監聽埠、建立連線、提供服務 - sync 加鎖
- goroutine 與 channel
- gob 的基本使用
執行
執行環境:go 1.13.1
- 開啟
GO111MODULE
,執行go mod download
安裝依賴 - 啟動 server:
server/cmd
目錄下執行go run main.go
- 啟動 client:
tui/cmd
目錄下執行go run main.go -server=localhost:3333
,可以在多個視窗啟動不同的客戶端
學習方法
本專案共有 5 個分支,對應 4 個階段。這 5 個分支依次為:
-
v0-template-code
:模板程式碼,包含詳細的註釋,但未實現相應的方法 -
v1-protocol-reader-writer
:實現了基於字串方式的協議的編解碼 -
v2-server-implementation
:實現了服務端 -
v3-client-implementation
:實現了客戶端 -
v4-gob-protocol
:使用 gob 作為通訊協議
對應的階段為:
- 實現基於字串的通訊協議:v0 -> v1
- 實現服務端:v1 -> v2
- 實現客戶端:v2 -> v3
- 使用 gob 作為通訊協議:v3 -> v4
建議從 v0 開始,根據註釋內容依次實現每個模組。如果實在摸不著頭腦,可以檢視 master 分支下的程式碼獲得提示。
步驟簡介
1. 實現協議
執行:
git checkout v0-template-code
複製程式碼
在這一步我們要實現通訊協議。TCP 傳輸的都是無格式的位元組流,我們需要定義這些字串的格式,以在客戶端和服務端能解析出相應的命令與引數。
客戶端與服務端通過 TCP 傳輸的是字串,因此需要規定一個將字串解析為命令的協議。
約定一條命令的格式如下所示:
[命令型別] [引數1] [引數2] ... [引數n]\n
複製程式碼
每條命令以換行符結尾。
命令一共有三種:
-
NAME
:客戶端設定使用者名稱 -
SEND
:客戶端傳送聊天訊息 -
MESSAGE
:服務端廣播聊天訊息給其他使用者
比如客戶端傳送聊天訊息的命令為:
SEND somemessage\n
複製程式碼
服務端廣播訊息給其他使用者的命令為:
MESSAGE username somemessage\n
複製程式碼
protocol/command.go
中定義了命令的型別。需要實現 protocol/reader.go
與 protocol/writer.go
中的相應方法。
2. 實現服務端
執行:
git checkout v1-protocol-reader-writer
複製程式碼
在這一步我們要實現服務端。服務端接受客戶端的連線請求,並儲存所有連線以在之後向客戶端傳送資料。
服務端的工作流程為:
- Listen:啟動一個伺服器,監聽一個埠
- Accept & Serve:與客戶端建立一個連線,併為其提供服務
- Remove:在客戶端退出連線後,刪除該客戶的的連線
- Close:停止監聽埠,關閉伺服器
服務端與客戶端的互動有:
- 接受客戶端發來的訊息,並廣播(broadcast)給其他客戶端
- 設定某個客戶端的名稱
server/server.go
將服務端的行為定義為一個介面,server/tcp_server.go
為相應的實現。
實現客戶端
執行:
git checkout v2-server-implementation
複製程式碼
在這一步我們要實現客戶端。客戶端其實比較簡單,只需要連線到服務端,然後向服務端傳送訊息或接收服務端的訊息。這部分的工作主要在於 UI 的實現,不過我沒有單獨抽離它。感興趣的讀者可以檢視 tui/ 目錄下的原始碼。
使用 gob 作為通訊協議
執行:
git checkout v3-client-implementation
複製程式碼
在這一步我們要使用 gob 作為通訊協議,替換原來的基於字串的協議。只需要修改 protocol/reader.go
和 protocol/writer.go
的 Read()
和 Write()
方法,涉及到 gob 的兩組方法:NewEncoder/Encoder
、NewDecoder/Decode
。是不是很方便!
下一步優化
這個專案還可以進一步優化,歡迎感興趣的讀者提交你的 PR!
- 使用 RPC 呼叫而不是 TCP 呼叫,如 grpc
- 資料持久化:使得後加入聊天室的人也能看到之前的聊天記錄,資料儲存方式可以為 gob 編碼
- 服務端支援多個聊天室
- 高階內容:支援斷線重連,可以檢視這篇文章
參考資料: