当前位置: 首页 > article >正文

21_HTML5 WebSocket --[HTML5 API 学习之旅]

HTML5 WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,它使得客户端和服务器之间可以保持一个持久连接,并允许双方实时地向对方发送数据。WebSocket 协议不同于传统的 HTTP 请求-响应模式,因为它建立了一个持久性的通道,减少了不必要的网络流量和延迟,特别适用于需要频繁更新的数据应用,如在线游戏、聊天应用、股票行情等。

以下是关于如何使用 HTML5 WebSocket 的一些基本概念和示例代码:

1. 创建 WebSocket 对象

要创建一个 WebSocket 连接,你需要实例化 WebSocket 对象,并传入服务器的 URL。

// 创建一个新的 WebSocket 实例
var socket = new WebSocket('ws://example.com/socketserver');

2. 监听事件

WebSocket 对象有四个主要的事件:openmessageerrorclose。你可以监听这些事件来处理不同的情况。

  • open: 当成功建立连接时触发。
  • message: 当从服务器收到消息时触发。
  • error: 当发生错误时触发。
  • close: 当连接关闭时触发。
socket.addEventListener('open', function (event) {
    console.log("Connection opened");
});

socket.addEventListener('message', function (event) {
    console.log('Message from server ', event.data);
});

socket.addEventListener('error', function (event) {
    console.error('Error occurred', event);
});

socket.addEventListener('close', function (event) {
    console.log('Connection closed');
});

3. 发送数据

一旦连接打开,你就可以通过调用 send() 方法向服务器发送数据。

socket.send('Hello Server!');

4. 关闭连接

当不再需要 WebSocket 连接时,可以通过调用 close() 方法来关闭它。

socket.close();

5. 处理二进制数据

WebSocket 不仅支持文本数据,还支持二进制数据传输。你可以发送和接收 BlobArrayBuffer 类型的数据。

// 发送二进制数据
var blob = new Blob(['some binary data'], { type: 'application/octet-stream' });
socket.send(blob);

// 接收二进制数据
socket.binaryType = 'arraybuffer'; // 设置为 arraybuffer 或 blob
socket.addEventListener('message', function(event) {
    if (event.data instanceof ArrayBuffer) {
        console.log('Received binary data:', event.data);
    }
});

注意事项

  • WebSocket URL 可以是 ws://(不加密)或 wss://(加密),类似于 HTTP 和 HTTPS。
  • 浏览器对 WebSocket 的支持存在差异,所以在生产环境中使用前,请确保检查目标浏览器的支持情况。
  • 如果 WebSocket 连接断开,可能需要实现重连机制以保证连接的稳定性。

以上就是使用 HTML5 WebSocket 的基础指南。WebSocket 提供了一种更高效、更快速的客户端与服务器之间的通信方式,对于需要实时交互的应用程序来说非常有用。

为了使用 HTML5 WebSocket 与 Gin 框架(Go语言的一个HTTP web框架)结合,我们可以创建几个简单的例子来演示如何在客户端和服务器之间建立 WebSocket 连接,并实现数据的实时传输。以下是三个不同场景的完整示例。

示例1:基本的 WebSocket 通信

Go Server (main.go)

package main

import (
	"log"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
)

// upgrader用于将HTTP连接升级到WebSocket连接
var upgrader = websocket.Upgrader{
	// CheckOrigin函数用于检查WebSocket的请求来源是否合法
	// 注意:在生产环境中应该更严格地检查来源
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

// wsHandler处理WebSocket请求
func wsHandler(c *gin.Context) {
	// 将HTTP连接升级到WebSocket连接
	conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		log.Print("upgrade:", err)
		return
	}
	defer conn.Close()

	// 无限循环读取WebSocket消息
	for {
		// 读取消息类型、消息内容及错误信息
		messageType, message, err := conn.ReadMessage()
		if err != nil {
			log.Println("read:", err)
			break
		}
		// 打印接收到的消息
		log.Printf("recv: %s", message)
		// 将接收到的消息写回给客户端
		err = conn.WriteMessage(messageType, message)
		if err != nil {
			log.Println("write:", err)
			break
		}
	}
}

// main函数是程序的入口点
func main() {
	// 创建默认的Gin路由器
	r := gin.Default()

	// 加载HTML模板文件夹中的所有HTML文件,以便可以在响应中使用这些模板。
	// 这里假设HTML文件位于项目根目录下的"templates"文件夹中。
	r.LoadHTMLGlob("templates/*")

	// 定义根路径"/"的GET请求处理器。
	// 当用户访问根URL时,将渲染并返回"index.html"模板。
	r.GET("/", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.html", nil)
	})

	// 路由处理WebSocket请求
	r.GET("/ws", wsHandler)

	// 在端口8080上运行服务器
	r.Run(":8080")
}

HTML Client (index.html)

<!DOCTYPE html>
<html>
<head>
    <title>WebSocket Example</title>
</head>
<body>
    <script>
        var ws = new WebSocket('ws://localhost:8080/ws');

        ws.onopen = function() {
            console.log('Connected to server');
            ws.send('Hello from client!');
        };

        ws.onmessage = function(event) {
            console.log('Received:', event.data);
        };

        ws.onclose = function() {
            console.log('Connection closed');
        };
    </script>
</body>
</html>

在这里插入图片描述

示例2:支持二进制数据传输

修改 wsHandler 函数以处理 ArrayBufferBlob 类型的数据。

为了实现一个支持二进制数据传输的 WebSocket 应用,使用 Gin 框架作为服务器端,我们需要确保客户端和服务端都能正确地处理 ArrayBufferBlob 类型的数据。以下是如何设置 Go 服务端(使用 Gin 和 Gorilla WebSocket)和 HTML5 客户端以支持二进制数据传输的示例。

Go Server (main.go)

在 Go 服务器端,我们需要修改 WebSocket 处理函数以接受和发送二进制消息。我们将使用 websocket.BinaryMessage 类型来标识二进制消息,并确保在读写时指定正确的消息类型。

package main

import (
	"log"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
)

// upgrader用于将HTTP连接升级到WebSocket连接。
// CheckOrigin函数被设置为允许任何来源的请求,这在生产环境中应该更加严格。
var upgrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool {
		return true // 注意:在生产环境中应该更严格地检查来源
	},
}

// wsHandler处理WebSocket连接。
// 它首先将HTTP连接升级到WebSocket连接,然后进入循环以读取客户端发送的消息,并将接收到的消息回显给客户端。
func wsHandler(c *gin.Context) {
	conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		log.Print("upgrade:", err)
		return
	}
	defer conn.Close()

	for {
		messageType, message, err := conn.ReadMessage()
		if err != nil {
			log.Println("read:", err)
			break
		}

		log.Printf("Received binary message of length %d", len(message))

		// 回显收到的消息给客户端
		if err = conn.WriteMessage(messageType, message); err != nil {
			log.Println("write:", err)
			break
		}
	}
}

// main是程序的入口点。
// 它初始化gin路由器,配置路由,并启动HTTP服务器。
func main() {
	r := gin.Default()

	// 加载HTML模板文件夹中的所有HTML文件,以便可以在响应中使用这些模板。
	// 这里假设HTML文件位于项目根目录下的"templates"文件夹中。
	r.LoadHTMLGlob("templates/*")

	// 定义根路径"/"的GET请求处理器。
	// 当用户访问根URL时,将渲染并返回"index.html"模板。
	r.GET("/", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.html", nil)
	})

	// 定义"/ws"路径的GET请求处理器,用于处理WebSocket连接。
	r.GET("/ws", wsHandler)

	// 启动HTTP服务器,监听8080端口。
	r.Run(":8080")
}

HTML Client (index.html)

在客户端,我们将创建一个表单允许用户选择文件,然后通过 WebSocket 发送文件内容作为二进制数据。同时,我们也会监听从服务器接收到的二进制数据并展示出来。

<!DOCTYPE html>
<html>
<head>
    <title>WebSocket Binary Data Example</title>
</head>
<body>
    <!-- 文件输入框,用于用户选择文件 -->
    <input type="file" id="fileInput">
    <!-- 按钮,用于触发发送文件的动作 -->
    <button onclick="sendFile()">Send File</button>
    <!-- 用于显示服务器响应的消息 -->
    <div id="response"></div>

    <script>
        // 创建 WebSocket 客户端实例,连接到指定的 WebSocket 服务器
        var ws = new WebSocket('ws://localhost:8080/ws');

        /**
         * 发送用户选择的文件到 WebSocket 服务器
         * 用户点击"Send File"按钮时调用此函数
         */
        function sendFile() {
            // 获取文件输入框
            var fileInput = document.getElementById('fileInput');
            // 获取用户选择的第一个文件
            var file = fileInput.files[0];
            // 如果没有选择文件,弹出警告并返回
            if (!file) {
                alert("Please select a file first.");
                return;
            }

            // 创建一个 FileReader 对象,用于读取文件内容
            var reader = new FileReader();
            // 当文件内容被成功读取为 ArrayBuffer 时的回调函数
            reader.onload = function(event) {
                // 将文件内容作为 ArrayBuffer 发送给 WebSocket
                ws.send(event.target.result);
            };
            // 读取文件内容为 ArrayBuffer
            reader.readAsArrayBuffer(file);
        }

        // 当接收到 WebSocket 服务器的消息时的回调函数
        ws.onmessage = function(event) {
            // 获取用于显示响应消息的 div 元素
            var responseDiv = document.getElementById('response');
            // 检查接收到的消息是否为二进制数据(ArrayBuffer)
            if (event.data instanceof ArrayBuffer) {
                // 展示接收到的二进制数据长度
                responseDiv.textContent = 'Received binary data of length ' + event.data.byteLength;
            } else {
                // 展示接收到的文本消息
                responseDiv.textContent = 'Received text message: ' + event.data;
            }
        };

        // 当 WebSocket 连接关闭时的回调函数
        ws.onclose = function() {
            // 在控制台输出连接关闭的消息
            console.log('Connection closed');
        };
    </script>
</body>
</html>

在这里插入图片描述

在这个例子中,当用户选择了文件并点击“Send File”按钮后,文件将被读取为 ArrayBuffer 并通过 WebSocket 发送到服务器。服务器端会回显相同的数据,客户端则会显示接收到的数据长度。

请注意,在实际应用中,你可能需要考虑如何有效地管理大文件的上传,比如分块上传、进度条显示等。此外,还需注意安全性问题,例如验证上传文件的类型和大小限制等。

示例3:实现简单的聊天应用

在客户端添加表单用于输入消息,并在服务端实现消息的转发逻辑。

为了使用 Gin 框架和 WebSocket 实现一个简单的聊天应用,我们需要在服务器端维护连接的客户端列表,并且每当有新消息时广播给所有在线用户。此外,我们还需要在客户端实现用户界面来发送和接收消息。下面是具体的实现步骤:

Go Server (main.go)

首先,创建一个 Go 服务端程序,它将负责处理 WebSocket 连接、管理连接的客户端以及广播消息。

package main

import (
	"log"
	"net/http"
	"sync"

	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
)

var (
	upgrader = websocket.Upgrader{
		CheckOrigin: func(r *http.Request) bool {
			return true // 注意:在生产环境中应该更严格地检查来源
		},
	}
	clients   = make(map[*websocket.Conn]bool) // 已连接的客户端集合
	broadcast = make(chan Message)             // 广播消息通道
	mutex     sync.Mutex                       // 互斥锁
)

type Message struct {
	Username string `json:"username"`
	Content  string `json:"content"`
}

func wsHandler(c *gin.Context) {
	conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		log.Print("upgrade:", err)
		return
	}

	// 添加新的客户端到集合中
	mutex.Lock()
	clients[conn] = true
	mutex.Unlock()

	// 启动监听从客户端接收消息的协程
	go listenForMsgs(conn)

	// 处理连接关闭
	// defer func() {
	// 	conn.Close()
	// 	mutex.Lock()
	// 	delete(clients, conn)
	// 	mutex.Unlock()
	// }()
}

func listenForMsgs(conn *websocket.Conn) {
	for {
		var msg Message
		err := conn.ReadJSON(&msg)
		if err != nil {
			log.Printf("error: %v", err)
			break
		}
		broadcast <- msg
	}
}

func handleMessages() {
	for {
		// 获取最新的消息
		msg := <-broadcast
		// 将最新消息发送给每个客户端
		mutex.Lock()
		for client := range clients {
			err := client.WriteJSON(msg)
			if err != nil {
				log.Printf("error: %v", err)
				client.Close()
				delete(clients, client)
			}
		}
		mutex.Unlock()
	}
}

func main() {
	go handleMessages()

	r := gin.Default()

	// 加载HTML模板文件夹中的所有HTML文件,以便可以在响应中使用这些模板。
	// 这里假设HTML文件位于项目根目录下的"templates"文件夹中。
	r.LoadHTMLGlob("templates/*")

	// 定义根路径"/"的GET请求处理器。
	// 当用户访问根URL时,将渲染并返回"index.html"模板。
	r.GET("/", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.html", nil)
	})

	r.GET("/ws", wsHandler)

	r.Run(":8080")
}

HTML Client (templates/index.html)

接下来是 HTML 客户端代码,它允许用户输入用户名和消息,并显示聊天记录。

<!DOCTYPE html>
<html>
<head>
    <title>WebSocket Chat Example</title>
    <style>
        #chat {
            height: 300px;
            overflow-y: scroll;
            border: 1px solid black;
            margin-bottom: 10px;
        }
    </style>
</head>
<body>
    <h1>WebSocket Chat</h1>
    <div id="chat"></div>
    <input type="text" id="username" placeholder="Username">
    <input type="text" id="messageInput" placeholder="Type a message">
    <button onclick="sendMessage()">Send</button>

    <script>
        var ws = new WebSocket('ws://localhost:8080/ws');
        var chatDiv = document.getElementById('chat');
        var usernameInput = document.getElementById('username');

        function sendMessage() {
            var input = document.getElementById('messageInput');
            var username = usernameInput.value || 'Anonymous';
            var msg = { username: username, content: input.value };
            ws.send(JSON.stringify(msg));
            input.value = '';
        }

        ws.onmessage = function(event) {
            var messages = document.createElement('div');
            var data = JSON.parse(event.data);
            messages.textContent = `${data.username}: ${data.content}`;
            chatDiv.appendChild(messages);
            chatDiv.scrollTop = chatDiv.scrollHeight; // 自动滚动到底部
        };

        ws.onclose = function() {
            console.log('Connection closed');
        };
    </script>
</body>
</html>

在这里插入图片描述

目录结构

确保你的项目有一个名为 static 的文件夹,里面包含 index.html 文件。这使得你可以通过 Gin 的 r.Static() 方法提供静态资源。

.
├── main.go
└── templates
    └── index.html

这个简单的聊天应用程序展示了如何使用 WebSocket 在客户端和服务端之间进行实时通信。每当用户发送一条消息时,该消息会被广播给所有已连接的客户端,并显示在聊天区域中。此示例还包含了基本的用户体验设计,例如自动滚动到最后一条消息。在实际应用中,你可能需要添加更多的功能,比如用户认证、消息持久化等。


http://www.kler.cn/a/460783.html

相关文章:

  • 前端路由 Hash 和 History 模式原理对比区别
  • Nacos服务注册和发现
  • ACL的注意事项
  • 深入理解Java中的Set集合:特性、用法与常见操作指南
  • checked 溢出问题
  • kafka怎么保证顺序消费?
  • 京准科普:车辆测速网络时间同步系统的概述
  • JAVA八股文-序列化和反序列化
  • vsCode调试Vue项目
  • HuatuoGPT-o1:基于40K可验证医学问题的两阶段复杂推理增强框架,通过验证器引导和强化学习提升医学模型的推理能力
  • 【亚马逊云】基于Amazon EC2实例部署 NextCloud 云网盘并使用 Docker-compose 搭建 ONLYOFFICE 企业在线办公应用软件
  • 混合精度训练(Mixed Precision Training)如何在 bf16 到 fp32 转换中保留信息:数值模拟与代码实现(中英双语)
  • 移动 APP 设计规范:构建高效、易用与美观的用户体验
  • 【2024年-10月-8日-开源社区openEuler实践记录】深度分析 Gala-Gopher:革新分布式系统运维的开源力量
  • archlinux使用
  • 力扣hot100——技巧
  • 小程序信息收集(小迪网络安全笔记~
  • FreeRTOS: ISR(中断服务例程)和 TCB(任务控制块)
  • Python面向对象编程全面解析
  • 大模型算法题(2)
  • wps透视数据表
  • 微信公众号 发布 接口405报错
  • 机器学习中的欠拟合
  • echarts 柱形图重叠柱形图legend,双y轴
  • Spring Boot教程之四十一:在 Spring Boot 中调用或使用外部 API
  • Kafka中的Topic和Partition有什么关系?