使用 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 协议》,转载必须注明作者和本文链接
请问楼主第二节出来了吗