使用 Go 和 ReactJS 构建聊天系统(二):简单通讯

现在我们已经建立了基本的前端和后端,是时候对它们进行实际操作了。

在本节中,我们将实现一个基本的 WebSocket 服务器。

在本节结束后,我们将拥有一个前端应用程序,它可以以双向方式与后端直接通信。

服务器

让我们从后端 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)
}

它将运行在 http://localhost:8080 上,可以通过 go run main.go 来启动它。如果你打开浏览器这个网址,你将会看到页面输出 Simple Server

WebSocket 协议

在我们开始之前,有必要介绍一下它的工作原理。

基本上,WebSocket 通过 TCP 套接字连接为我们提供了从不可信源到服务器的双向通信。从本质上讲,这意味着,我们不必继续轮询我们的 Web 服务器以获取更新,而且不必每次轮询都必须执行 TCP 握手,而是可以维护单个 TCP 套接字连接,然后在该套接字上发送和侦听消息。

这大大减少了任何实时应用程序所需的网络开销,并且使我们能够在单个服务器实例上维护数量惊人的客户端。

缺点

WebSocket 肯定会有一些缺点。引入状态后,跨多个实例扩展应用程序就变得更加复杂。

您必须考虑一些解决办法,比如将状态存储在消息代理中,或者存储在可以与应用程序实例并行扩展的数据库/内存缓存中。

实现

在实现 WebSocket 端点时,我们需要创建一个新的端点,然后将连接从标准 HTTP 端点升级为持久的 WebSocket 连接。

值得庆幸的是,gorilla/websocket 包提供了我们需要的功能,以便轻松地将 HTTP 连接升级为 WebSocket 连接。

注意 - 你可以在这里阅读更多关于官方 WebSocket 协议的信息: RFC-6455

创建 WebSocket 端点

既然我们已经涵盖了理论,那么让我们看看如何在实践中做到这一点。 让我们创建一个新的端点 /ws,将其从标准的 http 端点转换为 ws 端点。

这个端点将做三件事,它将检查我们传入的 HTTP 请求的来源,然后给打开我们的端点到每个客户端返回 true 。然后,我们将尝试使用定义的 upgrader 来升级连接。

最后,我们将开始侦听传入的消息,然后简单地将它们打印出来并回显到相同的连接。这将允许我们从我们新创建的 WebSocket 端点来验证我们的前端可以连接和发送/接收消息:

package main

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

    "github.com/gorilla/websocket"
)

// 我们去定义一个 Upgrader
// 这需要一个 Read 和 Write 的缓冲大小
var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
  WriteBufferSize: 1024,

  // 我们需要检查连接的来源
  // 这将允许我们根据我们的 React 发出请求
  // 现在,我们将不检查就允许任何连接
  CheckOrigin: func(r *http.Request) bool { return true },
}

// 定义 reader 来侦听
// 新消息将会发送到我们的 WebSocket
// 端点
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 前端发挥作用的地方。

我们将保持相对简单的风格,并定义一个包含 WebSocket 连接代码的 api/index.js 文件。

// 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 };

因此,在上面的代码中,我们定义了两个随后导出的函数。它们是 connect() 和 sendMsg(msg)

首先,连接到 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;

成功编译后,我们应该在浏览器中看到一个单独的按钮元素,并且如果打开浏览器控制台,还应该看到它已经能够成功连接到运行在 http://localhost:8080 上的后端 WebSocket 服务器。

问题 - 单击此按钮会发生什么? 您在浏览器的控制台和后端的控制台中看到什么输出?

总结

到此结束本系列的第 2 节。 我们已经能够创建一个非常简单的 WebSocket 服务器,该服务器回显发送给它的所有消息。

这一步是开发应用程序的关键的第一步,现在我们已经建立并运行了基础,我们可以开始着眼于实现基本的聊天功能并使应用程序有用!

本文译自tutorialedge

本作品采用《CC 协议》,转载必须注明作者和本文链接
最初的时候也是最苦的时候,最苦的时候也是最酷的时候。
讨论数量: 1

请问楼主第二节出来了吗

3年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!