使用Go和React实现Websocket,实现Web apps实时通信
一个简单的教程,让你在Web apps中实现实时通信
照片由Manson Yim在Unsplash上拍摄,作者修改
对于很多想在Web apps中实现实时通信的用户而言,这篇文章对你肯定大有裨益,它将引导你逐步了解如何使用Go和React实现WebSocket。
依赖项
所需的依赖项如下:
设置API服务器
首先,我们将使用echo包制作一个API服务器。
将以下代码添加到 main.go
:
package main
import (
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"net/http"
)
func main() {
e := echo.New()
e.Use(middleware.Logger())
e.Use(middleware.Recover())
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!")
})
e.Logger.Fatal(e.Start(":8080"))
}
它将在http:// localhost:8080 Hello, World!
进行响应,如下所示:
设置一个WebSocket
默认情况下,Go有一个专为WebSocket服务的软件包,但是目前,它涵盖的功能却没有人们口中所说的那么多:
“ 此程序包目前缺乏WebSocket程序包中可替代的和更积极维护的的某些功能:
https://godoc.org/github.com/gorilla/websocket
https://godoc.org/nhooyr.io/websocket ”
因此,我们将使用gorilla / websocket包来设置WebSocket
——这是Go中实现广泛使用的WebSocket。
实现WebSocket的步骤如下:
- 为WebSocket创建端点
- 升级传入的HTTP连接
- 侦听一个连接
为WebSocket创建一个端点
我们要做的第一件事是为Websocket创建一个端点。
让我们将WebSocket端点添加到 main.go
,如下所示:
func main() {
// ...
e.GET("/ws", func(c echo.Context) error {
return c.String(http.StatusOK, "Hi, WebSocket!")
})
}
升级传入的HTTP连接
接下来,我们将升级传入的HTTP连接。
要做到这一点,我们需要创建一个结构: websocket.Upgrader
。此代码的结构包含WebSocket连接的信息,如下所示:
var upgrader = websocket.Upgrader {}
func main(){}
并使用以下代码在处理程序中升级HTTP连接:
func main() {
e.GET("/ws", func(c echo.Context) error {
upgrader.CheckOrigin = func(r *http.Request) bool { return true }
ws, err := upgrader.Upgrade(c.Response().Writer, c.Request(), nil)
if !errors.Is(err, nil) {
log.Println(err)
}
defer ws.Close()
log.Println("Connected!")
return nil
})
}
侦听一个连接
接下来,我们将创建一个函数来侦听通过客户端发送的任何WebSocket连接。
在这种情况下,我们期望来自客户端应用程序的JSON数据,所以让我们创建一个名为 Message
的结构:
type Message struct {
Message string `json:"message"`
}
func main() {}
并在处理程序中添加一些代码:
e.GET("/ws", func(c echo.Context) error {
upgrader.CheckOrigin = func(r *http.Request) bool { return true }
ws, err := upgrader.Upgrade(c.Response().Writer, c.Request(), nil)
if !errors.Is(err, nil) {
log.Println(err)
}
defer ws.Close()
log.Println("Connected!")
for {
var message Message
err := ws.ReadJSON(&message)
if !errors.Is(err, nil) {
log.Printf("error occurred: %v", err)
break
}
log.Println(message)
// send message from server
if err := ws.WriteJSON(message); !errors.Is(err, nil) {
log.Printf("error occurred: %v", err)
}
}
return nil
})
定义 for
循环,该循环将侦听从客户端应用程序发送的任何消息。
读取JSON数据后,它将通过 ws.WriteJSON(message)
发送消息到客户端。
设置客户端应用程序
现在我们已经为WebSocket设置了一个端点,我们将创建一个客户端应用程序,方便通过WebSocket端点发送。
为了快速开始,我们将使用 create-react-app
:
npx create-react-app websocket-app --template typescript
安装后,通过运行以下命令来运行开发服务器:
yarn start
你将看到欢迎页面:
欢迎页面
连接到WebSocket端点
为了连接到我们的WebSocket端点,我们将创建一个WebSocket API实例在 ws://127.0.0.1:8080/ws
上进行连接,并尝试通过该连接发送消息。
对 src/App.tsx
进行一些修改,如下所示:
import React, { useCallback, useEffect, useState } from 'react';
import './App.css';
const socket = new WebSocket("ws://127.0.0.1:8080/ws");
function App() {
const [message, setMessage] = useState('')
const [inputValue, setInputValue] = useState('')
useEffect(() => {
socket.onopen = () => {
setMessage('Connected')
};
socket.onmessage = (e) => {
setMessage("Get message from server: " + e.data)
};
return () => {
socket.close()
}
}, [])
const handleClick = useCallback((e) => {
e.preventDefault()
socket.send(JSON.stringify({
message: inputValue
}))
}, [inputValue])
const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(e.target.value)
}, [])
return (
<div className="App">
<input id="input" type="text" value={inputValue} onChange={handleChange} />
<button onClick={handleClick}>Send</button>
<pre>{message}</pre>
</div>
);
}
export default App;
安装好后,App组件将使用 socket.onopen
事件启动侦听器,WebSocket将在该事件中升级连接。
而 socket.messsage
事件监听来自服务器的消息。
现在我们准备对其进行测试,访问http:// localhost:3000,然后让我们在字段中输入一些值:
检测连接
你可以看到连接成功运行。
但是,如果多个客户端同时发送消息怎么办?是否要在其他标签中同步呢?
检测连接 2
答案是否定的:因为每个客户端都有对应的连接。在 main.go
中,我们只向有一个关联连接的客户端发送消息。
在本文中,我们希望在整个应用程序中实现实时连接而无需重新加载。
为此,我们需要实现一个可以处理多个客户端并向其发送消息的实现。
处理多个客户的实现
首先,我们将创建 hub.go
,其中包含客户信息并向他们发送消息。我们将使用以下代码:
package main
import (
"errors"
"github.com/gorilla/websocket"
"log"
)
type Hub struct {
clients map[*websocket.Conn]bool
broadcast chan Message
}
func NewHub() *Hub {
return &Hub{
clients: make(map[*websocket.Conn]bool),
broadcast: make(chan Message),
}
}
func (h *Hub) run() {
for {
select {
case message := <-h.broadcast:
for client := range h.clients {
if err := client.WriteJSON(message); !errors.Is(err, nil) {
log.Printf("error occurred: %v", err)
}
}
}
}
}
并对 main.go
进行一些修改:

该
hub
结构存储客户端并通过通道发送消息。通道收到消息后,它将写回所有客户端。
现在,我们已经实现了一个侦听器,它将向所有选项卡发送消息。
让我们测试一下是否一切正常。
如你所见,一旦右侧的选项卡发送消息,左侧的选项卡将显示相同的消息。
现在,我们就已经实现了实时通信。
结论
我们已经介绍了如何使用Go和React实现WebSocket连接,从而在Web apps中实现实时通信。而WebSocket已经存在了一段时间,现在根据我可以使用的介绍,大多数现代浏览器都支持WebSocket 。
此外,GraphQL通过Subscriptions利用WebSocket协议,该操作可监视Graphql服务器发出的事件。
对于双向实时通信,WebSocket比旧的方式(例如HTTP轮询和SSE)更可靠、更高效。此外,借助上面已经看到的出色的库,你可以轻松地将WebSocket引入你的应用程序。
原文作者 Manato Kuroda
原文链接https://betterprogramming.pub/implementing-websocket-with-go-and-react-b3ee976770ab