1. 程式人生 > 程式設計 >?【Go 實踐】實現一個簡單的多人聊天室

?【Go 實踐】實現一個簡單的多人聊天室

簡介

Github 地址
部落格原文

本文使用 go 實現了一個多人聊天室,參考文章為 Writing a Chat Server in Go,點選檢視中文翻譯版

本文的特點在於:將原始專案分為自底向上的若干個階段,新手可以一步步地實現系統的不同模組,逐漸掌握相應知識點。原始碼裡有詳細的註釋,引導你在不看原始碼的情況下自己實現相應程式碼。

本文假設你:

  • 瞭解 go 語言的基本語法
  • 瞭解 git 的使用

完成本專案,你將學會這些內容:

  • Reader 方法的基本使用
  • 使用 net 包實現一個 tcp 伺服器:監聽埠、建立連線、提供服務
  • sync 加鎖
  • goroutine 與 channel
  • gob 的基本使用

執行

執行環境:go 1.13.1

  1. 開啟 GO111MODULE,執行 go mod download 安裝依賴
  2. 啟動 server:server/cmd 目錄下執行 go run main.go
  3. 啟動 client:tui/cmd 目錄下執行 go run main.go -server=localhost:3333,可以在多個視窗啟動不同的客戶端

學習方法

本專案共有 5 個分支,對應 4 個階段。這 5 個分支依次為:

  1. v0-template-code:模板程式碼,包含詳細的註釋,但未實現相應的方法
  2. v1-protocol-reader-writer:實現了基於字串方式的協議的編解碼
  3. v2-server-implementation:實現了服務端
  4. v3-client-implementation:實現了客戶端
  5. v4-gob-protocol:使用 gob 作為通訊協議

對應的階段為:

  1. 實現基於字串的通訊協議:v0 -> v1
  2. 實現服務端:v1 -> v2
  3. 實現客戶端:v2 -> v3
  4. 使用 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.goprotocol/writer.go 中的相應方法。

2. 實現服務端

執行:

git checkout v1-protocol-reader-writer
複製程式碼

在這一步我們要實現服務端。服務端接受客戶端的連線請求,並儲存所有連線以在之後向客戶端傳送資料。

服務端的工作流程為:

  1. Listen:啟動一個伺服器,監聽一個埠
  2. Accept & Serve:與客戶端建立一個連線,併為其提供服務
  3. Remove:在客戶端退出連線後,刪除該客戶的的連線
  4. Close:停止監聽埠,關閉伺服器

服務端與客戶端的互動有:

  1. 接受客戶端發來的訊息,並廣播(broadcast)給其他客戶端
  2. 設定某個客戶端的名稱

server/server.go 將服務端的行為定義為一個介面,server/tcp_server.go 為相應的實現。

實現客戶端

執行:

git checkout v2-server-implementation
複製程式碼

在這一步我們要實現客戶端。客戶端其實比較簡單,只需要連線到服務端,然後向服務端傳送訊息或接收服務端的訊息。這部分的工作主要在於 UI 的實現,不過我沒有單獨抽離它。感興趣的讀者可以檢視 tui/ 目錄下的原始碼。

使用 gob 作為通訊協議

執行:

git checkout v3-client-implementation
複製程式碼

在這一步我們要使用 gob 作為通訊協議,替換原來的基於字串的協議。只需要修改 protocol/reader.goprotocol/writer.goRead()Write() 方法,涉及到 gob 的兩組方法:NewEncoder/EncoderNewDecoder/Decode。是不是很方便!

下一步優化

這個專案還可以進一步優化,歡迎感興趣的讀者提交你的 PR!

  1. 使用 RPC 呼叫而不是 TCP 呼叫,如 grpc
  2. 資料持久化:使得後加入聊天室的人也能看到之前的聊天記錄,資料儲存方式可以為 gob 編碼
  3. 服務端支援多個聊天室
  4. 高階內容:支援斷線重連,可以檢視這篇文章

參考資料: