本節完整代碼: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 Server
。shell
在開始寫代碼以前,咱們須要瞭解一下理論。數據庫
WebSockets 能夠經過 TCP 鏈接進行雙工通訊。這讓咱們能夠經過單個 TCP 套接字來發送和監聽消息,從而避免經過輪詢 Web 服務器去通訊,每次輪詢操做都會執行 TCP 握手過程。
WebSockets 大大減小了應用程序所需的網絡帶寬,而且使得咱們在單個服務器實例上維護大量客戶端。
WebSockets 確定有一些值得考慮的缺點。好比一旦引入狀態,在跨多個實例擴展應用程序的時候就變得更加複雜。
在這種場景下須要考慮更多的狀況,例如將狀態存儲在消息代理中,或者存儲在數據庫/內存緩存中。
在實現 WebSocket 服務時,咱們須要建立一個端點,而後將該端點的鏈接從標準的 HTTP 升級到 WebSocket。
值得慶幸的是,gorilla/websocket
包提供了咱們所需的功能,能夠輕鬆地將 HTTP 鏈接升級到 WebSocket 鏈接。
注意 - 你能夠查看官方 WebSocket 協議的更多信息:RFC-6455
如今已經瞭解了理論,來看看如何去實踐。咱們建立一個新的端點 /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