【网络进阶】WebSocket协议

news2024/11/24 8:29:05

文章目录

    • 1. Web实时技术的应用
    • 2. WebSocket协议介绍
      • 2.1 WebSocket的工作原理
      • 2.2 优点
      • 2.3. 使用场景
      • 2.4 实现细节
    • 3. WebSocket服务器实现
      • 3.1 客户端代码(HTML & JavaScript)
      • 3.2 服务器端代码(C++)
      • 3.3 测试结果

1. Web实时技术的应用

实时Web技术在许多应用场景中具有重要意义,它们使得用户可以立即获得最新的数据和信息,从而提高了用户体验。以下是一些实时Web技术的典型应用:

  1. 聊天和通讯:在线聊天应用、企业通讯软件以及社交网络平台都需要实时技术来实现用户间的即时通信。一些知名的聊天应用,如WhatsApp、微信、Telegram和Slack等,都采用了实时技术。

  2. 在线协作:在线文档编辑、实时画板、项目管理工具等需要多个用户同时对文档或项目进行协作。谷歌文档(Google Docs)和微软的Office 365就是在线协作的典型例子。

  3. 实时消息推送:新闻网站、博客、股票行情、天气预报等应用需要实时向用户推送最新的数据和信息。通过实时技术,用户无需手动刷新页面,就可以接收到最新的内容。

  4. 在线游戏:多人在线游戏、实时策略游戏以及虚拟现实应用都需要实时技术来保证玩家之间的交互。实时技术可以降低延迟,提高游戏的流畅性和可玩性。

  5. 物联网(IoT):实时Web技术可以用于监控和控制物联网设备。例如,智能家居系统可以实时反馈家庭设备的状态,用户可以随时控制家电设备。

  6. 实时分析:实时数据分析、数据可视化和大数据处理需要实时技术来支持。例如,网站访问统计、销售数据分析、服务器性能监控等应用需要实时显示数据。

  7. 实时音视频通信:语音通话、视频会议和直播应用需要实时技术来传输音视频数据。这些应用通常使用WebRTC这样的实时通信技术。

  8. 实时定位和导航:基于位置的服务、地图应用和交通信息系统需要实时技术来获取和更新位置数据。例如,Uber和高德地图等应用需要实时显示用户和车辆的位置。

实时Web技术在许多领域都有广泛应用,它们为用户提供了更加丰富、更加实时的网络体验。在未来,随着5G、边缘计算等技术的普及,实时Web技术将会更加普遍地应用于各种场景。

2. WebSocket协议介绍

WebSocket协议是一种基于TCP的通信协议,它提供了一种全双工、低延迟的通信方式,使得客户端和服务器之间可以进行双向实时数据交换。WebSocket协议的设计目的是为了在Web浏览器和Web服务器之间提供更高效、更实时的双向通信。

WebSocket协议与HTTP协议不同,它使用了自己独立的协议标识符“ws”(不加密)或“wss”(加密),例如:ws://example.com 或 wss://example.com。WebSocket协议最初是作为HTML5标准的一部分提出的,现在已经被广泛应用于Web应用程序、在线游戏、实时通讯等领域。

2.1 WebSocket的工作原理

  1. 建立连接:首先,客户端会向服务器发送一个HTTP请求,请求中包含“Upgrade”和“Connection”头字段,表示希望将连接升级为WebSocket。如果服务器同意升级,它会返回一个状态码为101的HTTP响应,表示连接已经升级。

  2. 数据帧:在WebSocket连接建立后,客户端和服务器可以开始发送和接收数据。数据在WebSocket中以“帧”为单位传输,每个帧都包含一个标识符、负载长度和负载数据。标识符用于指示帧的类型(例如文本、二进制数据或控制帧)。

  3. 控制帧:WebSocket协议中有一些特殊的控制帧,用于管理连接的状态。例如,“关闭”帧用于通知对方关闭连接,“Ping”帧用于检测连接是否仍然活跃,“Pong”帧用作对“Ping”帧的响应。

  4. 关闭连接:当客户端或服务器希望关闭连接时,它们会发送一个“关闭”帧。收到“关闭”帧的一方应当回应一个“关闭”帧,然后双方都可以关闭TCP连接。在某些情况下,WebSocket连接可能因为网络原因或其他问题意外断开,这种情况下并不会发送“关闭”帧。

2.2 优点

  1. 双向实时通信:WebSocket提供了全双工通信,允许客户端和服务器同时发送和接收数据,从而实现实时交互。
  2. 低延迟:与基于HTTP的轮询或长轮询等技术相比,WebSocket能够大大降低数据交换的延迟。
  3. 减少网络开销:由于WebSocket在建立连接后可以保持长连接,因此可以减少频繁建立和关闭连接导致的额外网络开销。
  4. 易于集成:WebSocket与现有的Web技术(如HTML、CSS和JavaScript)兼容,因此开发者可以在不改变现有Web应用架构的前提下轻松地将WebSocket集成进来。

2.3. 使用场景

  1. 实时消息推送:例如聊天应用、新闻推送、股票行情等。
  2. 在线游戏:例如多人在线游戏、实时策略游戏等。
  3. 即时协作:例如在线文档编辑、实时画板等。
  4. 物联网(IoT):WebSocket可以用于实时监控和控制物联网设备。
  5. 其他实时应用:任何需要实时数据交换的场景都可以考虑使用WebSocket。

2.4 实现细节

  1. 安全性:为了提高安全性,可以使用加密的WebSocket连接(wss://)。此外,建议在服务器端实施适当的认证和授权策略。
  2. 跨域问题:WebSocket允许跨域连接,但需要注意的是,服务器端应检查请求头中的“Origin”字段以防止恶意连接。
  3. 心跳检测:为了确保连接的可靠性,可以定期发送“Ping”帧进行心跳检测,以便在连接中断时及时处理。
  4. 浏览器兼容性:虽然大多数现代浏览器都支持WebSocket,但仍需注意一些较旧版本的浏览器可能不支持。在这种情况下,可以考虑使用一些库(如Socket.IO)来提供自动降级到其他传输方式的功能。

3. WebSocket服务器实现

3.1 客户端代码(HTML & JavaScript)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebSocket Client</title>
    <style>
        #output {
            border: 1px solid #ccc;
            padding: 10px;
            width: 400px;
            height: 200px;
            overflow-y: scroll;
        }
    </style>
</head>
<body>
    <h1>WebSocket Client</h1>
    <label for="serverAddress">Server address:</label>
    <input type="text" id="serverAddress" value="ws://localhost">
    <label for="serverPort">Port:</label>
    <input type="number" id="serverPort" value="3000">
    <button id="connectBtn">Connect</button>
    <div id="output"></div>
    <button id="clearBtn">Clear</button><br>
    <input type="text" id="message" placeholder="Type your message...">
    <button id="sendBtn">Send</button>

    <script>
        function getBrowserInfo() {
            return {
                userAgent: navigator.userAgent,
                language: navigator.language,
                platform: navigator.platform,
                vendor: navigator.vendor,
            };
        }

        let ws;
        const serverAddress = document.getElementById('serverAddress');
        const serverPort = document.getElementById('serverPort');
        const connectBtn = document.getElementById('connectBtn');
        const output = document.getElementById('output');
        const message = document.getElementById('message');
        const sendBtn = document.getElementById('sendBtn');
        const clearBtn = document.getElementById('clearBtn');

        clearBtn.addEventListener('click', () => {
            output.innerHTML = '';
        });

        function appendOutput(text) {
            output.innerHTML += `${text}<br>`;
            output.scrollTop = output.scrollHeight;
        }

        connectBtn.addEventListener('click', () => {
            const address = `${serverAddress.value}:${serverPort.value}`;
            ws = new WebSocket(address);
            connectBtn.disabled = true;

            ws.addEventListener('open', () => {
                appendOutput(`Connected to server at ${address}`);
                ws.send(JSON.stringify(getBrowserInfo()));
            });

            ws.addEventListener('message', (event) => {
                appendOutput(`Server: ${event.data}`);
            });

            ws.addEventListener('close', () => {
                appendOutput('Disconnected from server');
                connectBtn.disabled = false;
            });

            ws.addEventListener('error', (event) => {
                appendOutput('Error: ' + event.message);
            });
        });

        sendBtn.addEventListener('click', () => {
            if (ws && ws.readyState === WebSocket.OPEN) {
                ws.send(message.value);
                appendOutput(`You: ${message.value}`);
                message.value = '';
            } else {
                appendOutput('Please connect to the server first.');
            }
        });

        message.addEventListener('keyup', (event) => {
            if (event.key === 'Enter') {
                sendBtn.click();
            }
        });
    </script>
</body>
</html>

3.2 服务器端代码(C++)

#include <boost/asio.hpp>
#include <boost/beast.hpp>
#include <boost/beast/websocket.hpp>
#include <iostream>
#include <string>
#include <thread>
#include <nlohmann/json.hpp>

namespace beast = boost::beast;
namespace http = beast::http;
namespace websocket = beast::websocket;
namespace net = boost::asio;
using tcp = net::ip::tcp;
using json = nlohmann::json;

constexpr auto DEFAULT_PORT = "3000";

void handleSession(websocket::stream<tcp::socket> ws) {
  try {
    ws.accept();
    auto remote_endpoint = ws.next_layer().socket().remote_endpoint();
    std::cout << "Connected to client: " << remote_endpoint << std::endl;

    for (;;) {
      beast::flat_buffer buffer;
      ws.read(buffer);

      auto message = beast::buffers_to_string(buffer.data());
      std::cout << "Received: " << message << std::endl;

      // Check if received message is browser information
      try {
        auto browser_info = json::parse(message);
        if (browser_info.contains("userAgent") && browser_info.contains("language") &&
            browser_info.contains("platform") && browser_info.contains("vendor")) {
          std::cout << "Browser Information:\n";
          std::cout << "  User Agent: " << browser_info["userAgent"].get<std::string>() << std::endl;
          std::cout << "  Language: " << browser_info["language"].get<std::string>() << std::endl;
          std::cout << "  Platform: " << browser_info["platform"].get<std::string>() << std::endl;
          std::cout << "  Vendor: " << browser_info["vendor"].get<std::string>() << std::endl;
          continue;
        }
      } catch (...) {
        // Not a valid JSON or not containing browser information
      }

      ws.text(ws.got_text());
      ws.write(buffer.data());
    }
  } catch (beast::system_error const &se) {
    if (se.code() != websocket::error::closed)
      std::cerr << "Error: " << se.code().message() << std::endl;
  } catch (std::exception const &e) {
    std::cerr << "Error: " << e.what() << std::endl;
  }
}

int main(int argc, char *argv[]) {
  try {
    auto const address = net::ip::make_address("0.0.0.0");
    auto const port = static_cast<unsigned short>(std::atoi(argc == 2 ? argv[1] : DEFAULT_PORT));

    net::io_context ioc{1};
    tcp::acceptor acceptor{ioc, {address, port}};
    std::cout << "WebSocket server is listening on " << address << ":" << port << std::endl;

    for (;;) {
      tcp::socket socket{ioc};
      acceptor.accept(socket);
      std::thread{handleSession, websocket::stream<tcp::socket>{std::move(socket)}}.detach();
    }
  } catch (std::exception const &e) {
    std::cerr << "Error: " << e.what() << std::endl;
    return EXIT_FAILURE;
  }
}

注意如上代码中使用了Boost.Beast库来实现WebSocket和nlohmann/json的JSON库来处理JSON数据,Boost库的安装之前已经讲解过,下面说明nlohmann/json库的安装:

  1. 使用以下命令安装 wget(如果尚未安装):
sudo yum install wget
  1. 使用 wget 下载 nlohmann/json 库的单文件版本:
wget https://github.com/nlohmann/json/releases/download/v3.10.4/json.hpp

注意:请确保下载与您的代码兼容的版本。在此示例中,我下载了 3.10.4 版本。您可以在 此处 查找其他版本。

  1. 创建一个包含 nlohmann/json 的头文件的目录:
sudo mkdir -p /usr/local/include/nlohmann
  1. 将下载的 json.hpp 文件移动到刚刚创建的目录中:
sudo mv json.hpp /usr/local/include/nlohmann/

现在,nlohmann/json库已经安装在您的CentOS 7.6系统上。在C++代码中,您可以通过包含以下头文件来使用它:

#include <nlohmann/json.hpp>

在编译时,确保链接器可以找到nlohmann/json库的头文件。这通常不需要额外的编译器标志,但如果需要,您可能需要添加-I参数以指定头文件的安装路径。例如:

g++ your_code.cpp -o your_program -I/usr/local/include

3.3 测试结果

客户端:
在这里插入图片描述
服务器端:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/494597.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt 智能指针介绍: QSharedPointer、QWeakPointer 、QScopedPointer 、QPointer(附实例)

文章目录 1. 概述2. Qt 中有几种智能指针&#xff1f;2.1 QSharedPointer 实例2.2 QSharedPointer 与 QWeakPointer 实例2.3 QScopedPointer 实例2.4 QPointer 实例 1. 概述 在使用动态内存分配的情况下&#xff0c;需要确保对象的所有权正确地被管理和转移。使用智能指针可以…

【HarmonyOS】【FAQ】HarmonyOS应用开发相关问题解答(一)

【前提简介】 本文档主要总结HarmonyOS开发过程中可能遇到的一些问题解答&#xff0c;主要围绕HarmonyOS展开&#xff0c;包括但不限于不同API版本HarmonyOS开发、UI组件、DevEco Studio、Gitee示例代码等&#xff0c;并将持续更新哦。 【官方FAQ】 【FAQ】HarmonyOS应用及服…

(十二)地理数据库创建——基本组成项及数据加载

地理数据库创建——基本组成项及数据加载 目录 地理数据库创建——基本组成项及数据加载 1.建立数据库中的基本组成项1.1建立要素数据集1.2建立要素类1.2.1建立简单要素类1.2.2建立关系表 1.3建立关系表 2.向地理数据库加载数据2.1导入数据2.1.1导入Shapefile2.1.2导入dBASE 表…

数据结构:顺序表的增、删,查、改实现

1.概念 顺序表是用一段 物理地址连续 的存储单元依次存储数据元素的线性结构&#xff0c;一般情况下采用数组存 储。在数组上完成数据的增删查改。 2.分类 顺序表一般可以分为&#xff1a; 2.1 静态顺序表&#xff1a;使用定长数组存储元素 这样会造成空间给多了浪费&#x…

ThreadLocal初探

一、ThreadLocal介绍 一、官方介绍 从Java官方文档中的描述&#xff1a;ThreadLocal类用来提供线程内部的局部变量&#xff0c;这种变量在多线程环境下访问&#xff08;通过get和set方法访问&#xff09;时&#xff0c;能够保证各个线程的变量相对独立于其他线程内的变量。Thr…

apple pencil必须要买吗?性价比平替电容笔排行榜

要知道&#xff0c;真正的苹果原装Pencil&#xff0c;价格实在是太贵了&#xff0c;普通的消费者根本买不起。所以&#xff0c;有没有可能出现一种平替的、功能一模一样的、与苹果Pencil一样的电容笔呢&#xff1f;这倒也是。国产的平替笔和苹果Pencil的笔相比&#xff0c;并没…

Wireless-Sensor-Network-master_WSN_无线传感网络(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 近年来&#xff0c;随着对等网络、云计算和网格计算等分布式环境的发展&#xff0c;无线传感器网络&#xff08;WSN&#xff0…

10分钟吃透,python操作mysql数据库的增、删、改、查

大家好&#xff0c;我是csdn的博主&#xff1a;lqj_本人 这是我的个人博客主页&#xff1a; lqj_本人的博客_CSDN博客-微信小程序,前端,python领域博主lqj_本人擅长微信小程序,前端,python,等方面的知识https://blog.csdn.net/lbcyllqj?spm1011.2415.3001.5343哔哩哔哩欢迎关注…

聊聊汽车OTA测试技术方案

汽车OTA已成为时下热门话题&#xff0c;由于OTA的升级可能会带来一定的风险&#xff0c;针对OTA的测试就尤为重要。本文我们主要通过介绍OTA的发展背景、汽车OTA测试的必要性以及汽车OTA测试内容&#xff0c;为大家分享一套成熟的OTA测试方案。 什么是OTA OTA&#xff08;Over-…

以最大速度将数据迁移至AWS S3存储

数据上云&#xff0c;已经成为企业数据管理发展的必然趋势。 对于企业而言&#xff0c;数据上云“常态化”的趋势&#xff0c;无疑是一个巨大的技术红利。而数据规模爆发式增长的今天&#xff0c;移动和访问云端数据却成为困扰企业的一大难题。不过幸运的是&#xff0c;在对象…

ES6:promise简单学习

一、理解promise Promise将异步执行的程序变成同步执行&#xff0c;所谓的在开发中解决回调嵌套的问题 Promise 是异步编程的一种解决方案 从语法上讲&#xff0c;promise是一个对象 从它可以获取异步操作的消息 从本意上讲&#xff0c;它是承诺&#xff0c;承诺它过一段时间会…

Java入门超详细(内含Java基础 Java高级)

Java基础入门 - 内含Java基础&#xff0c;Java高级 Java 基本介绍Java 学习须知Java 学习文档Java 基础Java 基础语法Java 对象与类Java 基本数据类型Java 变量类型Java 修饰符Java 运算符Java 循环结构Java 条件语句Java switch caseJava 数组Java 日期与时间Java 正则表达式J…

Swift AsyncSequence — 代码实例详解

前言 AsyncSequence 是并发性框架和 SE-298 提案的一部分。它的名字意味着它是一个提供异步、顺序和迭代访问其元素的类型。换句话说&#xff1a;它是我们在 Swift 中熟悉的常规序列的一个异步变体。 就像你不会经常创建你的自定义序列一样&#xff0c;我不期望你经常创建一个…

2022 Jiangsu Collegiate Programming Contest A. PENTA KILL!

题目链接 Sample 1 Input 10 Bin Guigo Grevthar Bin GALA Grevthar GALA TitaN GALA Guigo GALA Aegis GALA Jojo GALA Grevthar Xiaohu Grevthar GALA Aegis Output PENTA KILL! Sample 3 Input 7 GALA Jojo Aegis Ming GALA Grevthar GALA Grevthar GALA Aegis GALA Guigo…

树莓派硬件介绍及配件选择

目录 树莓派Datasheet下载地址&#xff1a; Raspberry 4B 外观图&#xff1a; 技术规格书&#xff1a; 性能介绍&#xff1a; 树莓派配件选用 电源的选用&#xff1a; 树莓派外壳选用&#xff1a; 内存卡/U盘选用 树莓派Datasheet下载地址&#xff1a; Raspberry Pi …

CompletableFutrue异步处理

异步处理 一、线程的实现方式 1. 线程的实现方式 1.1 继承Thread class ThreadDemo01 extends Thread{Overridepublic void run() {System.out.println("当前线程:" Thread.currentThread().getName());} }1.2 实现Runnable接口 class ThreadDemo02 implements …

故障:更新后电脑卡顿

前一天下班的时候关电脑&#xff0c;关机选项变成了“更新并关机”&#xff0c;没多想&#xff0c;我点了。。。。早上上班就发现电脑卡得不行&#xff0c;看了下更新日志&#xff0c;装了一大堆东西&#xff0c;看了下任务管理器&#xff0c;内存直接跑到了90%&#xff0c;这电…

每日学术速递5.2

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.DataComp: In search of the next generation of multimodal datasets 标题&#xff1a;DataComp&#xff1a;寻找下一代多模态数据集 作者&#xff1a;Samir Yitzhak Gadre, Gab…

【JAVA模块六------ 综合案例基础巩固】

JAVA模块六------ 综合案例基础巩固 1 逢7跳过2 数组元素求和3 判断两个数组的内容是否相同4 查找某个数组元素索引5 数组元素反转输出&#xff1a;&#xff08;逆序输出&#xff09;6 评委打分7 随机产生验证码其他&#xff1a;方法抽取&#xff1a; 1 逢7跳过 要求&#xff1…

国产光伏仪器 6581太阳能电池板伏安特性测试仪

6581太阳能电池板伏安特性测试仪主要用于太阳能电池板生产的最终测试&#xff0c;也可以作为层压前测试使用&#xff0c;能大大提高一次封装成品率。该测试仪适合于单晶、多晶、薄膜等多种电池组件&#xff0c;可进行I-V曲线、P-V曲线、短路电流、开路电压、峰值功率等全部参数…