[譯] 使用 Go 和 ReactJS 構建聊天系統 (二)

咔嘰咔嘰發表於2019-08-02

本節完整程式碼:GitHub

本文是使用 ReactJS 和 Go 來構建聊天應用程式的系列文章的第 2 部分。你可以在這裡找到第 1 部分 - 初始化設定

現在我們已經建立好了基本的前端和後端,現在需要來完善一些功能了。

在本節中,我們將實現一個基於 WebSocket 的伺服器。

在該系列教程結束時,我們將有一個可以與後端雙向通訊的前端應用程式。

服務

我們可以使用 github.com/gorilla/websocket 包來設定 WebSocket 服務以及處理 WebSocket 連線的讀寫操作。

這需要在我們的 backend/ 目錄中執行此命令來安裝它:

$ go get github.com/gorilla/websocket
複製程式碼

一旦我們成功安裝了這個包,我們就可以開始構建我們的 Web 服務了。我們首先建立一個非常簡單的 net/http 服務:

package main

import (
    "fmt"
    "net/http"
)

func setupRoutes() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Simple Server")
    })
}

func main() {
    setupRoutes()
    http.ListenAndServe(":8080", nil)
}
複製程式碼

可以通過呼叫 go run main.go 來啟動服務,該服務將監聽 http://localhost:8080 。如果用瀏覽器開啟此連線,可以看到輸出 Simple Server

WebSocket 協議

在開始寫程式碼之前,我們需要了解一下理論。

WebSockets 可以通過 TCP 連線進行雙工通訊。這讓我們可以通過單個 TCP 套接字來傳送和監聽訊息,從而避免通過輪詢 Web 伺服器去通訊,每次輪詢操作都會執行 TCP 握手過程。

WebSockets 大大減少了應用程式所需的網路頻寬,並且使得我們在單個伺服器例項上維護大量客戶端。

連線

WebSockets 肯定有一些值得考慮的缺點。比如一旦引入狀態,在跨多個例項擴充套件應用程式的時候就變得更加複雜。

在這種場景下需要考慮更多的情況,例如將狀態儲存在訊息代理中,或者儲存在資料庫/記憶體快取中。

實現

在實現 WebSocket 服務時,我們需要建立一個端點,然後將該端點的連線從標準的 HTTP 升級到 WebSocket。

值得慶幸的是,gorilla/websocket 包提供了我們所需的功能,可以輕鬆地將 HTTP 連線升級到 WebSocket 連線。

注意 - 你可以檢視官方 WebSocket 協議的更多資訊:RFC-6455

建立 WebSocket 服務端

現在已經瞭解了理論,來看看如何去實踐。我們建立一個新的端點 /ws,我們將從標準的 http 端點轉換為 ws 端點。

此端點將執行 3 項操作,它將檢查傳入的 HTTP 請求,然後返回 true 以開啟我們的端點到客戶端。然後,我們使用定義的 upgrader 升級為 WebSocket 連線。

最後,我們將開始監聽傳入的訊息,然後將它們列印出來並將它們傳回相同的連線。這可以讓我們驗證前端連線並從新建立的 WebSocket 端點來傳送/接收訊息:

package main

import (
    "fmt"
    "log"
    "net/http"

    "github.com/gorilla/websocket"
)

// 我們需要定義一個 Upgrader
// 它需要定義 ReadBufferSize 和 WriteBufferSize
var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
  WriteBufferSize: 1024,

  // 可以用來檢查連線的來源
  // 這將允許從我們的 React 服務向這裡發出請求。
  // 現在,我們可以不需要檢查並執行任何連線
  CheckOrigin: func(r *http.Request) bool { return true },
}

// 定義一個 reader 用來監聽往 WS 傳送的新訊息
func reader(conn *websocket.Conn) {
    for {
    // 讀訊息
        messageType, p, err := conn.ReadMessage()
        if err != nil {
            log.Println(err)
            return
        }
    // 列印訊息
        fmt.Println(string(p))

        if err := conn.WriteMessage(messageType, p); err != nil {
            log.Println(err)
            return
        }

    }
}

// 定義 WebSocket 服務處理函式
func serveWs(w http.ResponseWriter, r *http.Request) {
    fmt.Println(r.Host)

  // 將連線更新為 WebSocket 連線
    ws, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Println(err)
  }

  // 一直監聽 WebSocket 連線上傳來的新訊息
    reader(ws)
}

func setupRoutes() {
  http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Simple Server")
  })

  // 將 `/ws` 端點交給 `serveWs` 函式處理
    http.HandleFunc("/ws", serveWs)
}

func main() {
    fmt.Println("Chat App v0.01")
    setupRoutes()
    http.ListenAndServe(":8080", nil)
}
複製程式碼

如果沒有問題的話,我們使用 go run main.go 來啟動服務。

客戶端

現在已經設定好了服務,我們需要一些能夠與之互動的東西。這是我們的 ReactJS 前端發揮作用的地方。

我們先儘量讓客戶端保持簡單,並定義一個 api/index.js 檔案,它將包含 WebSocket 連線的程式碼。

// api/index.js
var socket = new WebSocket("ws://localhost:8080/ws");

let connect = () => {
  console.log("Attempting Connection...");

  socket.onopen = () => {
    console.log("Successfully Connected");
  };

  socket.onmessage = msg => {
    console.log(msg);
  };

  socket.onclose = event => {
    console.log("Socket Closed Connection: ", event);
  };

  socket.onerror = error => {
    console.log("Socket Error: ", error);
  };
};

let sendMsg = msg => {
  console.log("sending msg: ", msg);
  socket.send(msg);
};

export { connect, sendMsg };
複製程式碼

因此,在上面的程式碼中,我們定義了我們隨後匯出的 2 個函式。分別是 connect()sendMsg(msg)

第一個函式,connect() 函式,連線 WebSocket 端點,並監聽例如與 onopen 成功連線之類的事件。如果它發現任何問題,例如連線關閉的套接字或錯誤,它會將這些問題列印到瀏覽器控制檯。

第二個函式,sendMsg(msg) 函式,允許我們使用 socket.send() 通過 WebSocket 連線從前端傳送訊息到後端。

現在我們在 React 專案中更新 App.js 檔案,新增對 connect() 的呼叫並建立一個觸發 sendMsg() 函式的 <button /> 元素。

// App.js
import React, { Component } from "react";
import "./App.css";
import { connect, sendMsg } from "./api";

class App extends Component {
  constructor(props) {
    super(props);
    connect();
  }

  send() {
    console.log("hello");
    sendMsg("hello");
  }

  render() {
    return (
      <div className="App">
        <button onClick={this.send}>Hit</button>
      </div>
    );
  }
}

export default App;
複製程式碼

使用 npm start 成功編譯後,我們可以在瀏覽器中看到一個按鈕,如果開啟瀏覽器控制檯,還可以看到成功連線的 WebSocket 服務執行在 http://localhost:8080

問題 - 單擊此按鈕會發生什麼?你在瀏覽器的控制檯和後端的控制檯中看到了什麼輸出?

總結

結束了本系列的第 2 部分。我們已經能夠建立一個非常簡單的 WebSocket 服務,它可以回顯傳送給它的任何訊息。

這是開發應用程式的關鍵一步,現在我們已經啟動並執行了基本框架,我們可以開始考慮實現基本的聊天功能並讓這個程式變得更有用!

下一節:Part 3 - 前端實現


原文:tutorialedge.net/projects/ch…

作者:Elliot Forbes 譯者:咔嘰咔嘰 校對:polaris1119

本文由 GCTT 原創編譯,Go 中文網 榮譽推出

[譯] 使用 Go 和 ReactJS 構建聊天系統 (二)

相關文章