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

本節完整代碼:GitHubcss

本文是使用 ReactJS 和 Go 來構建聊天應用程序的系列文章的第 2 部分。你能夠在這裏找到第 1 部分 - 初始化設置html

如今咱們已經創建好了基本的前端和後端,如今須要來完善一些功能了。前端

在本節中,咱們將實現一個基於 WebSocket 的服務器。react

在該系列教程結束時,咱們將有一個能夠與後端雙向通訊的前端應用程序。git

服務

咱們可使用 github.com/gorilla/websocket 包來設置 WebSocket 服務以及處理 WebSocket 鏈接的讀寫操做。github

這須要在咱們的 backend/ 目錄中運行此命令來安裝它:golang

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

一旦咱們成功安裝了這個包,咱們就能夠開始構建咱們的 Web 服務了。咱們首先建立一個很是簡單的 net/http 服務:web

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 Servershell

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 中文網 榮譽推出

相關文章
相關標籤/搜索