《Go 简易速速上手小册》第8章:网络编程(2024 最新版)(下)

简介: 《Go 简易速速上手小册》第8章:网络编程(2024 最新版)

《Go 简易速速上手小册》第8章:网络编程(2024 最新版)(上)+https://developer.aliyun.com/article/1487003


8.3 WebSocket 与 RPC - Go 语言的深海通信线

在Go语言的并发海洋中,WebSocket和RPC (Remote Procedure Call) 是两种深海通信线,允许我们跨越深渊,进行实时和跨服务的通信。

8.3.1 基础知识讲解

WebSocket

WebSocket提供了一个全双工通信渠道,允许客户端和服务器之间建立持久连接并实时交换数据。这对于需要实时功能的应用来说非常有用,比如在线聊天室、实时数据仪表板等。

在Go中,gorilla/websocket是一个流行的库,用于在HTTP服务器上添加WebSocket支持。

RPC

RPC允许客户端执行远程服务器上的函数就像是执行本地函数一样,隐藏了网络请求的复杂性。Go标准库中的net/rpc包提供了构建RPC系统的基础。

8.3.2 重点案例:实时聊天应用

在这个扩展案例中,我们将构建一个简单的实时聊天应用,使用WebSocket实现客户端和服务器之间的实时通信。这个应用将允许用户通过Web浏览器连接到聊天服务器,发送消息并实时接收来自其他用户的消息。

服务端实现

我们使用gorilla/websocket库来处理WebSocket连接。首先,需要安装这个库:

go get -u github.com/gorilla/websocket

然后,我们实现聊天服务器的核心逻辑:

// chatserver/main.go
package main
import (
    "github.com/gorilla/websocket"
    "net/http"
    "log"
)
var clients = make(map[*websocket.Conn]bool) // 连接到服务器的客户端
var broadcast = make(chan []byte)            // 广播通道
var upgrader = websocket.Upgrader{
    CheckOrigin: func(r *http.Request) bool {
        return true
    },
}
func main() {
    http.HandleFunc("/ws", handleConnections)
    go handleMessages()
    log.Println("Chat server started on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}
func handleConnections(w http.ResponseWriter, r *http.Request) {
    ws, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Fatal(err)
    }
    defer ws.Close()
    clients[ws] = true
    for {
        _, msg, err := ws.ReadMessage()
        if err != nil {
            log.Printf("Error: %v", err)
            delete(clients, ws)
            break
        }
        broadcast <- msg
    }
}
func handleMessages() {
    for {
        msg := <-broadcast
        for client := range clients {
            err := client.WriteMessage(websocket.TextMessage, msg)
            if err != nil {
                log.Printf("Error: %v", err)
                client.Close()
                delete(clients, client)
            }
        }
    }
}

在这个实现中,handleConnections函数处理新的WebSocket连接,读取来自客户端的消息,并将它们放入broadcast通道。handleMessages函数监听broadcast通道,将接收到的消息发送给所有连接的客户端。

客户端实现

客户端可以是一个简单的HTML页面,使用JavaScript与WebSocket服务器进行通信:

<!-- chatclient/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Go Chat App</title>
    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var ws = new WebSocket("ws://localhost:8080/ws");
            var messages = document.getElementById("messages");
            ws.onmessage = function(event) {
                var message = document.createElement("p");
                message.textContent = event.data;
                messages.appendChild(message);
            };
            document.getElementById("sendBtn").onclick = function() {
                var message = document.getElementById("messageInput").value;
                ws.send(message);
                document.getElementById("messageInput").value = "";
            };
        });
    </script>
</head>
<body>
    <div id="messages"></div>
    <input id="messageInput" type="text">
    <button id="sendBtn">Send</button>
</body>
</html>

这个HTML页面包含一个消息列表、一个文本输入框和一个发送按钮。当用户点击发送按钮时,当前的消息会通过WebSocket发送到服务器,并且清空输入框。当服务器通过WebSocket发送消息时,它们会被添加到页面的消息列表中。

运行示例

  1. 启动服务端:在chatserver目录下运行go run main.go,启动聊天服务器。
  2. 打开客户端:在浏览器中打开chatclient/index.html文件,连接到聊天服务器。

通过这个案例,我们展示了如何使用Go和WebSocket构建一个简单的实时聊天应用。这个应用能够让多个用户通过Web界面实时交换消息,体验到实时通信的魅力。随着你继续探索WebSocket和Go在网络编程方面的更多可能性,你将能够构建更加复杂和强大的实时Web应用。

8.3.3 拓展案例 1:股票行情实时更新服务

在这个案例中,我们将构建一个基于WebSocket的股票行情实时更新服务。该服务允许客户端通过WebSocket订阅特定的股票代码,并在股票价格发生变动时接收实时更新。

服务端实现

为了简化演示,我们假设股票价格的变动是随机模拟的。在实际应用中,你可能需要连接到真实的股市数据API来获取实时行情。

首先,安装gorilla/websocket库:

go get -u github.com/gorilla/websocket

接着,实现WebSocket服务端逻辑:

// stockservice/main.go
package main
import (
    "encoding/json"
    "github.com/gorilla/websocket"
    "log"
    "math/rand"
    "net/http"
    "time"
)
var upgrader = websocket.Upgrader{
    CheckOrigin: func(r *http.Request) bool {
        return true
    },
}
type StockUpdate struct {
    Symbol string  `json:"symbol"`
    Price  float64 `json:"price"`
}
func handleConnections(w http.ResponseWriter, r *http.Request) {
    ws, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Fatal(err)
    }
    defer ws.Close()
    // 模拟股票代码列表
    stocks := []string{"AAPL", "GOOGL", "MSFT"}
    for {
        // 等待客户端消息并读取订阅的股票代码
        _, msg, err := ws.ReadMessage()
        if err != nil {
            log.Printf("error: %v", err)
            break
        }
        symbol := string(msg)
        // 如果订阅的股票代码在模拟的列表中,开始发送更新
        for _, s := range stocks {
            if s == symbol {
                for {
                    // 随机生成股票价格并发送
                    price := rand.Float64() * 1000
                    update := StockUpdate{Symbol: symbol, Price: price}
                    updateJSON, err := json.Marshal(update)
                    if err != nil {
                        log.Printf("error: %v", err)
                        break
                    }
                    if err := ws.WriteMessage(websocket.TextMessage, updateJSON); err != nil {
                        log.Printf("error: %v", err)
                        break
                    }
                    // 每隔一秒发送一次更新
                    time.Sleep(1 * time.Second)
                }
                break
            }
        }
    }
}
func main() {
    rand.Seed(time.Now().UnixNano())
    http.HandleFunc("/ws", handleConnections)
    log.Println("Stock service started on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

客户端示例

客户端可以是一个简单的HTML页面,通过JavaScript与WebSocket服务器建立连接,并发送订阅请求:

<!-- stockclient/index.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Real-time Stock Updates</title>
    <script>
        document.addEventListener("DOMContentLoaded", () => {
            const ws = new WebSocket("ws://localhost:8080/ws");
            ws.onopen = () => {
                console.log("Connected to the server");
                // 订阅AAPL股票的更新
                ws.send("AAPL");
            };
            ws.onmessage = (event) => {
                const stockUpdate = JSON.parse(event.data);
                console.log(`Stock update for ${stockUpdate.symbol}: $${stockUpdate.price.toFixed(2)}`);
            };
            ws.onerror = (error) => {
                console.log("WebSocket error: " + error.message);
            };
            ws.onclose = () => {
                console.log("Disconnected from the server");
            };
        });
    </script>
</head>
<body>
    <h1>Real-time Stock Updates</h1>
    <p>Open the console to view the stock updates.</p>
</body>
</html>

在这个HTML页面中,当页面加载完成后,客户端通过WebSocket连接到服务器并发送一个订阅请求(在此例中为"AAPL"股票)。然后,它将监听服务器发送的更新,并在控制台中显示更新的股票价格。

运行示例

  1. 启动服务端:在stockservice

录下运行go run main.go,启动股票行情更新服务。

  1. 打开客户端:在Web浏览器中打开stockclient/index.html文件,并查看浏览器控制台以接收实时的股票价格更新。

通过这个拓展案例,我们演示了如何使用WebSocket在Go中实现一个实时的股票行情更新服务。客户端可以订阅感兴趣的股票代码,并接收关于这些股票的实时价格更新,展现了WebSocket在构建实时通信应用中的强大能力。继续探索WebSocket和Go的其他网络编程特性,为你的应用带来更加丰富的实时交互体验。

8.3.4 拓展案例 2:远程系统监控

在这个案例中,我们将构建一个基于RPC的远程系统监控工具,允许管理员从中央服务器调用远程机器上的函数,以获取系统状态信息,如CPU使用率、内存使用等。这个工具将使用Go的net/rpc包来实现RPC通信。

功能描述

  1. 远程获取系统状态:允许管理员远程调用函数,获取目标机器的系统状态信息。
  2. 支持多种状态查询:支持查询CPU使用率、内存使用情况等不同类型的系统状态。
  3. 简单的认证机制:实现一个简单的认证机制,确保只有授权的用户可以查询系统状态。

服务端实现

首先,我们需要定义提供的远程调用方法和服务:

// monitorserver/main.go
package main
import (
    "errors"
    "net"
    "net/rpc"
    "runtime"
)
type Args struct {
    AuthToken string
}
type SystemStats struct {
    CPU    string
    Memory string
}
type Monitor int
func (t *Monitor) GetSystemStats(args *Args, reply *SystemStats) error {
    if args.AuthToken != "secret" {
        return errors.New("unauthorized")
    }
    // 模拟获取系统状态
    reply.CPU = "2.4 GHz"
    reply.Memory = "8 GB"
    return nil
}
func main() {
    monitor := new(Monitor)
    rpc.Register(monitor)
    rpc.HandleHTTP()
    l, err := net.Listen("tcp", ":1234")
    if err != nil {
        panic(err)
    }
    for {
        conn, err := l.Accept()
        if err != nil {
            continue
        }
        go rpc.ServeConn(conn)
    }
}

在这个实现中,我们定义了一个Monitor类型,它有一个方法GetSystemStats,用于远程获取系统状态。我们使用了一个简单的认证机制,通过检查传递的AuthToken来授权访问。

客户端实现

客户端将通过RPC调用服务端的GetSystemStats方法来获取系统状态:

// monitorclient/main.go
package main
import (
    "fmt"
    "log"
    "net/rpc"
)
type Args struct {
    AuthToken string
}
type SystemStats struct {
    CPU    string
    Memory string
}
func main() {
    client, err := rpc.DialHTTP("tcp", "localhost:1234")
    if err != nil {
        log.Fatal("Dialing:", err)
    }
    args := &Args{"secret"}
    var reply SystemStats
    err = client.Call("Monitor.GetSystemStats", args, &reply)
    if err != nil {
        log.Fatal("Monitor error:", err)
    }
    fmt.Printf("CPU: %s\nMemory: %s\n", reply.CPU, reply.Memory)
}

在这个客户端实现中,我们创建了一个RPC客户端,连接到服务端,并调用Monitor.GetSystemStats方法,传递认证令牌并获取系统状态信息。

运行示例

  1. 启动服务端:在monitorserver目录下运行go run main.go启动系统监控服务。
  2. 运行客户端:在monitorclient目录下运行go run main.go来从服务端获取系统状态信息。

通过这个拓展案例,我们演示了如何使用Go的net/rpc包实现一个基于RPC的远程系统监控工具。这个工具允许管理员远程获取系统状态信息,展现了RPC在构建分布式系统和服务通信中的实用性。继续探索Go的RPC和其他网络编程特性,为你的分布式应用提供强大的后端支持。

目录
相关文章
|
6天前
|
监控 Linux PHP
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
54 20
|
11天前
|
监控 关系型数据库 MySQL
【01】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-硬件设备实时监控系统运营版发布-本产品基于企业级开源项目Zabbix深度二开-分步骤实现预计10篇合集-自营版
【01】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-硬件设备实时监控系统运营版发布-本产品基于企业级开源项目Zabbix深度二开-分步骤实现预计10篇合集-自营版
22 0
|
2月前
|
Go 数据安全/隐私保护 UED
优化Go语言中的网络连接:设置代理超时参数
优化Go语言中的网络连接:设置代理超时参数
|
3月前
|
数据库连接 Go 数据库
Go语言中的错误注入与防御编程。错误注入通过模拟网络故障、数据库错误等,测试系统稳定性
本文探讨了Go语言中的错误注入与防御编程。错误注入通过模拟网络故障、数据库错误等,测试系统稳定性;防御编程则强调在编码时考虑各种错误情况,确保程序健壮性。文章详细介绍了这两种技术在Go语言中的实现方法及其重要性,旨在提升软件质量和可靠性。
57 1
|
3月前
|
网络协议 安全 Go
Go语言进行网络编程可以通过**使用TCP/IP协议栈、并发模型、HTTP协议等**方式
【10月更文挑战第28天】Go语言进行网络编程可以通过**使用TCP/IP协议栈、并发模型、HTTP协议等**方式
90 13
|
3月前
|
网络协议 安全 Go
Go语言的网络编程基础
【10月更文挑战第28天】Go语言的网络编程基础
82 8
|
3月前
|
安全 网络协议 Go
Go语言网络编程
【10月更文挑战第28天】Go语言网络编程
144 65
|
3月前
|
网络协议 Go
Go语言网络编程的实例
【10月更文挑战第27天】Go语言网络编程的实例
45 7
|
3月前
|
缓存 网络协议 Unix
Go语言网络编程技巧
【10月更文挑战第27天】Go语言网络编程技巧
52 8
|
6月前
|
安全 Java Go
为什么选择Go语言编写网络应用程序
为什么选择Go语言编写网络应用程序

热门文章

最新文章