教你使用Websockets和Go編程語言構建實時聊天應用程序

現代網頁應用程序正日趨豐富而複雜。像這樣有趣又有活力的體驗很受用戶歡迎。用戶無需向服務器發起調用,或刷新瀏覽器,就可以讓頁面實時更新。早期的開發者依賴 AJAX 來創建具備近乎實時體驗的應用程序。而現在,他們運用 WebSockets 就能創建完全實時的應用程序了。

本教程中我們將使用 Go 編程語言以及 WebSockets 來創建一個實時的聊天應用程序。前端將會使用 HTML5 和 VueJS 來編寫。該內容需要你對 Go 語言, JavaScript 以及 HTML5 有一個基礎的瞭解,最好有一點點使用 VueJS 的經驗。

WebSocket 是什麼?

通常 Web 應用使用一個或多個請求對 HTTP 服務器提供對外服務。客戶端軟件通常是 Web 瀏覽器向服務器發送請求,服務器發回一個響應。響應通常是 HTML 內容,由瀏覽器來渲染為頁面。樣式表,JavaScript 代碼和圖像也可以在響應中發送回來以完成整個網頁。每個請求和響應都屬於特定的單獨的連接的一部分,像 Facebook 這樣的大型網站為了渲染單個頁面實際上可以產生數百個這樣的連接。

AJAX 的工作方式跟這個完全相同。使用 JavaScript,開發人員可以向 HTTP 服務器請求一小段信息,然後根據響應更新部分頁面。這可以在不刷新瀏覽器的情況下完成,但仍然存在一些限制。

每個 HTTP 請求/響應的連接在被響應之後都會關閉,因此獲得任何新的信息必須新建另一個連接。如果沒有新的請求發送給服務器,它就不知道客戶端正在查找新的信息。能讓 AJAX 應用程序看起來像實時的一種技術是定時循環發送 AJAX 請求。在設置了時間間隔之後,應用程序可以重新將請求發送到服務器,以查看是否有任何更新需要反饋給瀏覽器。這比較適合小型應用程序,但並不高效。這時候 WebSockets 就派上用場了。WebSockets 是由 Internet 工程任務組(IETF)創建的建議標準的一部分。 RFC6455 中詳細描述了 WebSockets 實現的完整技術規範。下面是該文檔定義 WebSocket 的節選:

WebSocket 協議用於客戶端代碼和遠程主機之間進行通信,其中客戶端代碼是在可控環境下的非授信代碼


換句話說,WebSocket 是一個總是打開的連接,允許客戶端和服務器自發地來回發送消息。服務器可在必要時將新信息推送到客戶端,客戶端也可以對服務器執行相同操作。


JavaScript 中的 WebSockets


大多數現代瀏覽器都在其 JavaScript 實現中支持 WebSockets。要從瀏覽器中啟動一個 WebSocket 連接,你可以使用簡單的 WebSocket JavaScript 對象,如下:


教你使用Websockets和Go編程語言構建實時聊天應用程序


您唯一需要的參數是一個 URL,WebSocket 連接可通過此 URL 連接服務器。該請求實際是一個 HTTP 請求,但為了安全連接我們使用“ws://”或“wss://”。這使服務器知道我們正在嘗試創建一個新的 WebSocket 連接。之後服務器將“升級”該客戶端和服務之間的連接到永久的雙向連接。一旦新的 WebSocket 對象被創建,並且連接成功創建之後,我們就可以使用“send()”方法發送文本到服務器,並在 WebSocket 的“onmessage”屬性上定義一個處理函數來處理從服務器發送的消息。具體邏輯會在之後的聊天應用程序代碼中解釋。


Go 中的 WebSockets


WebSockets 並不包含在 Go 標準庫中,但幸運的是有一些不錯的第三方包讓 WebSockets 的使用輕而易舉。在這個例子中,我們將使用一個名為“gorilla/websocket”的包,它是流行的 Gorilla Toolkit 包集合的一部分,多用於在 Go 中創建 Web 應用程序。請運行以下命令進行安裝:


教你使用Websockets和Go編程語言構建實時聊天應用程序

構建服務器

這個應用程序的第一部分是服務器。這是一個處理請求的簡單 HTTP 服務器。它將為我們提供 HTML5 和 JavaScript 代碼,以及建立客戶端的 WebSocket 連接。另外,服務器還將跟蹤每個 WebSocket 連接並通過 WebSocket 連接將聊天信息從一個客戶端發送到所有其他客戶端。首先創建一個新的空目錄,然後在該目錄中創建一個“src”和“public”目錄。在“src”目錄中創建一個名為“main.go”的文件。搭建服務器首先要進行一些設置。我們像所有 Go 應用程序一樣啟動應用程序,並定義包命名空間,在本例中為“main”。接下來我們導入一些有用的包。 “log”和“net/http”都是標準庫的一部分,將用於日誌記錄並創建一個簡單的 HTTP 服務器。最終包“github.com/gorilla/websocket”將幫助我們輕鬆創建和使用 WebSocket 連接。

教你使用Websockets和Go編程語言構建實時聊天應用程序

下面的兩行代碼是一些全局變量,在應用程序的其它地方會被用到。全局變量的實踐較差,不過這次為了簡單起見我們還是使用了它們。第一個變量是一個 map 映射,其鍵對應是一個指向 WebSocket 的指針,其值就是一個布爾值。我們實際上並不需要這個值,但使用的映射數據結構需要有一個映射值,這樣做更容易添加和刪除單項。

第二個變量是一個用於由客戶端發送消息的隊列,扮演通道的角色。在後面的代碼中,我們會定義一個 goroutine 來從這個通道讀取新消息,然後將它們發送給其它連接到服務器的客戶端。


教你使用Websockets和Go編程語言構建實時聊天應用程序

接下來我們創建一個 upgrader 的實例。這只是一個對象,它具備一些方法,這些方法可以獲取一個普通 HTTP 鏈接然後將其升級成一個 WebSocket,稍後會有相關代碼介紹。


教你使用Websockets和Go編程語言構建實時聊天應用程序

最後我們將定義一個對象來管理消息,數據結構比較簡單,帶有一些字符串屬性,一個 email 地址,一個用戶名以及實際的消息內容。我們將利用 email 來展示 Gravatar 服務所提供的唯一身份標識。

由反引號包含的文本是 Go 在對象和 JSON 之間進行序列化和反序列化時需要的元數據。

教你使用Websockets和Go編程語言構建實時聊天應用程序

Go 應用程序的主要入口總是 "main()" 函數。代碼非常簡潔。我們首先創建一個靜態的文件服務,並將之與 "/" 路由綁定,這樣用戶訪問網站時就能看到 index.html 和其它資源。在這個示例中我們有一個保存 JavaScript 代碼的 "app.js" 文件和一個保存樣式的 "style.css" 文件。


教你使用Websockets和Go編程語言構建實時聊天應用程序

我們想定義的下一個路由是 "/ws",在這裡處理啟動 WebSocket 的請求。我們先向處理函數傳遞一個函數的名稱,"handleConnections",稍後再來定義這個函數。

教你使用Websockets和Go編程語言構建實時聊天應用程序

下一步就是啟動一個叫 "handleMessages" 的 Go 程序。這是一個並行過程,獨立於應用和其它部分運行,從廣播頻道中取得消息並通過各客戶端的 WebSocket 連接傳遞出去。並行是 Go 中一項強大的特性。關於它如何工作的內容超出了這篇文章的範圍,不過你可以自行查看 Go 的官方教程網站。如果你熟悉 JavaScript,可聯想一下並行過程,作為後臺過程運行的 Go 程序,或 JavaScript 的異步函數。


教你使用Websockets和Go編程語言構建實時聊天應用程序

最後,我們向控制檯打印一個輔助信息並啟動 Web 服務。如果有錯誤發生,我們就把它記錄下來然後退出應用程序。


教你使用Websockets和Go編程語言構建實時聊天應用程序

接下來我們創建一個函數處理傳入的 WebSocket 連接。首先我們使用升級的 "Upgrade()" 方法改變初始的 GET 請求,使之成為完全的 WebSocket。如果發生錯誤,記錄下來,但不退出。同時注意 defer 語句,它通知 Go 在函數返回的時候關閉 WebSocket。這是個不錯的方法,它為我們節省了不少可能出現在不同分支中返回函數前的 "Close()" 語句。

教你使用Websockets和Go編程語言構建實時聊天應用程序

接下來把新的客戶端添加到全局的 "clients" 映射表中進行註冊,這個映射表在早先已經創建了。

教你使用Websockets和Go編程語言構建實時聊天應用程序

最後一步是一個無限循環,它一直等待著要寫入 WebSocket 的新消息,將其從 JSON 反序列化為 Message 對象然後送入廣播頻道。然而 "handleMessages()" Go 程序就能把它送給連接中的其它客戶端。

如果從 socket 中讀取數據有誤,我們假設客戶端已經因為某種原因斷開。我們記錄錯誤並從全局的 “clients” 映射表裡刪除該客戶端,這樣一來,我們不會繼續嘗試與其通信。

另外,HTTP 路由處理函數已經被作為 goroutines 運行。這使得 HTTP 服務器無需等待另一個連接完成,就能處理多個傳入連接。

教你使用Websockets和Go編程語言構建實時聊天應用程序

服務器的最後一部分是 "handleMessages()"函數。這是一個簡單循環,從“broadcast”中連續讀取數據,然後通過各自的 WebSocket 連接將消息傳播到所以客戶端。同樣,如果寫入 Websocket 時出現錯誤,我們將關閉連接,並將其從“clients” 映射中刪除。

教你使用Websockets和Go編程語言構建實時聊天應用程序

構建客戶端

如果沒有漂亮的 UI,聊天應用程序將無法完成。 我們需要使用一些 HTML5 和 VueJS 來創建一個簡單、乾淨的界面,再利用一些諸如 Materialize CSS 和 EmojiOn 的庫來生成一些樣式和表情符號。 在“public”目錄中,創建一個名為“index.html”的新文件。第一部分很基礎。為了美觀,我們也會放入一些樣式表和字體。“style.css”是自定義的樣式表,用於自定義一些內容。

教你使用Websockets和Go編程語言構建實時聊天應用程序

下一部分僅與接口相關,其中只包含一些用於選擇用戶名、發送消息和顯示新的聊天信息的字段。與 VueJS 交互的細節超出本文的介紹範圍,你可閱讀此文檔瞭解更多。

教你使用Websockets和Go編程語言構建實時聊天應用程序

最後一步只需要導入所有需要的 JavaScript 庫,包括 Vue、EmojiOne、jQuery 和 Materialize。我們需要 MD5 庫獲取來自 Gravatar 的頭像 URL,這用 JavaScript 代碼寫出來就好理解了。最後導入 "app.js"。

教你使用Websockets和Go編程語言構建實時聊天應用程序

然後在 "public" 目錄下創建一個 "style.css" 文件。其中會放入一些樣式。

教你使用Websockets和Go編程語言構建實時聊天應用程序

客戶端的最後一部分是 JavaScript 代碼。在 "public" 目錄下創建文件 "app.js"。

對於 VueJS 應用程序來說,一開始都是創建新的 Vue 對象。我們將它與 id 為 "#app" 的 div 綁定。這會讓 div 內的所有東西與 Vue 實現共享作用域。下面定義一些變量。

教你使用Websockets和Go編程語言構建實時聊天應用程序

Vue 提供了一個叫 "created" 的屬性,這是一個函數,會在 Vue 實例剛剛創建時調用。這裡非常適合對應用做一些設置工作。在這個示例中我們希望創建一個新的 WebSocket 連接與服務器連接,並創建一個處理器用於處理從服務器發送過來的消息。我們把新的 WebSocket 對象保存在 "data" 屬性的 "ws" 變量中。

"addEventListener()"方法接受一個用於處理傳入消息的函數。我們期望所有消息都是 JSON 字符串,以便統一解析為一個對象字面量。然後我們可以用各個屬性和 avater 頭像一起組成漂亮的消息行。"gravatarURL()" 方法會在後面詳述。我們用了一個叫 EmojiOne 的表情庫來解析emoji 代碼。"toImage()" 方法會把 emoji 代碼轉換為實際的圖片。比如,如果你輸入 ":robot:",它會被替換為一個機器人 emoji 表情圖。

教你使用Websockets和Go編程語言構建實時聊天應用程序

"methods" 屬性可以定義各種函數,我們會在 VueJS 應用中使用這些函數。"send"方法用於向服務器發送消息。我們先確保消息不是空的,然後把消息組織成一個對象,再用"stringify"把它變成 JSON 字符串,以便服務器能正確解析。我們使用 jQuery 來處理傳入消息中 HTML 和 JavaScript 中的特殊字符,以防止各種類型的注入攻擊。

教你使用Websockets和Go編程語言構建實時聊天應用程序

"join"函數會確保用戶在發送消息前輸入 email 地址和用戶名。一旦他們輸入了這些信息,我們將 joined 設置為 "true",同時允許他們開始交談。同樣,我們會處理 HTML 和 JavaScript 的特殊字符。

教你使用Websockets和Go編程語言構建實時聊天應用程序

最後一個函數是一個很好的輔助函數,用於從 Gravatar 獲取頭像。URL 的最後一段需要用戶的 email 地址的 MD5 編碼。MD5 是一種加密算法,它能隱藏 email 地址同時還能讓 email 地址作為一個唯一標識來使用。

教你使用Websockets和Go編程語言構建實時聊天應用程序

運行應用程序

要運行該應用程序,請打開控制檯窗口並確保進入應用程序的“src”目錄中,然後運行以下命令。

教你使用Websockets和Go編程語言構建實時聊天應用程序

教你使用Websockets和Go編程語言構建實時聊天應用程序

接下來打開 Web 瀏覽器並導航到“http://localhost:8000”站點。 然後就會顯示聊天屏幕,你可以在聊天屏幕中輸入電子郵件和用戶名。

教你使用Websockets和Go編程語言構建實時聊天應用程序

如果要查看該應用多個用戶之間的通信方式,只需另外打開一個瀏覽器標籤頁或窗口,然後導航到“http://localhost:8000”。 輸入不同的電子郵件和用戶名。然後輪流從兩個窗口發送消息,這樣就可以看到多個用戶之間的通信方式了。

教你使用Websockets和Go編程語言構建實時聊天應用程序

結論

這只是一個基本的聊天應用程序,你可以在此基礎上進行更多的改進,添加更多的其他功能,並上傳源代碼,期待你能實現新用戶加入或者離開聊天時的私人提醒或者通知。盡情創造吧,此處不設限!我希望這篇文章對你有所幫助,並希望藉此啟發你使用 WebSockets 和 Go 開始創建自己的實時應用程序。



分享到:


相關文章: