学懂C++(四十一):网络编程——深入详解 C++ 网络编程之 WebSocket 应用技术

本文主要是介绍学懂C++(四十一):网络编程——深入详解 C++ 网络编程之 WebSocket 应用技术,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

一、引言

二、WebSocket 概念

1. WebSocket 概述

2. WebSocket 协议

WebSocket 握手请求示例

三、WebSocket 工作原理

四、WebSocket 的实现方式

1. 准备工作

2. WebSocket 服务器实现

服务器代码

3. WebSocket 客户端实现

客户端代码

五、总结


一、引言

        在现代互联网应用中,即时通信、实时数据传输和交互变得越来越重要。传统的 HTTP 协议由于其无状态和请求/响应模型的限制,难以满足这类需求。WebSocket 协议应运而生,提供了持久的双向通信通道。本文将深入解析 WebSocket 的概念、工作原理及其在 C++ 中的实现,并结合经典实例进行讲解。

二、WebSocket 概念
1. WebSocket 概述

        WebSocket 是一种全双工的通信协议,设计用于在 Web 浏览器和服务器之间进行实时、低延迟的双向通信。相较于传统的 HTTP 协议,WebSocket 协议具有以下几个显著特点:

  • 持久连接:一旦连接建立,客户端和服务器之间可以持续通信,避免了频繁的连接建立和关闭过程。
  • 低开销:减少了 HTTP 请求/响应头部的开销,适合高频率数据交换。
  • 双向通信:允许客户端和服务器随时发送数据,支持实时应用场景。
2. WebSocket 协议

        WebSocket 协议通过 HTTP/1.1 协议进行初始握手,然后升级到 WebSocket 协议。握手成功后,通信双方可以通过 TCP 连接进行双向数据传输。

WebSocket 握手请求示例
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

WebSocket 握手响应示例

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
三、WebSocket 工作原理

WebSocket 的工作流程主要包括以下几个步骤:

  1. 建立连接:客户端发起 HTTP 请求,包含特定的头部字段,用于请求将连接升级为 WebSocket 协议。
  2. 协议升级:服务器接收到请求后,返回相应的头部字段,确认升级为 WebSocket 协议。
  3. 数据传输:建立 WebSocket 连接后,客户端和服务器可以通过该连接进行双向数据传输,数据帧以二进制或文本格式传输。
  4. 关闭连接:任意一方可以随时关闭连接。
四、WebSocket 的实现方式

        WebSocket 的实现方式可以分为客户端和服务器两部分。在 C++ 中,可以使用开源的 WebSocket 库(如 libwebsocketsBoost.Beast)进行实现。本文将使用 Boost.AsioBoost.Beast 库来实现 WebSocket 客户端和服务器。

1. 准备工作

在开始编写代码之前,请确保已经安装了 Boost 库。可以从 Boost 官方网站下载并安装。

在 Linux 上

在基于 Debian 的系统(如 Ubuntu)上,可以使用以下命令安装 Boost 库:

sudo apt-get update sudo apt-get install libboost-all-dev
在 Windows 上

在 Windows 上,可以使用 vcpkg 或从 Boost 官网下载源码并手动编译安装。以下是使用 vcpkg 的示例:

  1. 下载并安装 vcpkg
  2. 使用 vcpkg 安装 Boost 库:
vcpkg install boost-asio boost-beast
2. WebSocket 服务器实现
服务器代码
#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <string>
#include <thread>namespace beast = boost::beast;         // from <boost/beast.hpp>
namespace http = beast::http;           // from <boost/beast/http.hpp>
namespace websocket = beast::websocket; // from <boost/beast/websocket.hpp>
namespace net = boost::asio;            // from <boost/asio.hpp>
using tcp = net::ip::tcp;               // from <boost/asio/ip/tcp.hpp>// 会话类
class session : public std::enable_shared_from_this<session> {websocket::stream<beast::tcp_stream> ws_;beast::flat_buffer buffer_;public:explicit session(tcp::socket socket): ws_(std::move(socket)) {}// 启动异步操作void run() {// 执行 WebSocket 握手ws_.async_accept(beast::bind_front_handler(&session::on_accept, shared_from_this()));}private:// WebSocket 握手的回调函数void on_accept(beast::error_code ec) {if (ec) {std::cerr << "accept: " << ec.message() << std::endl;return;}// 读取消息do_read();}// 读取消息void do_read() {ws_.async_read(buffer_, beast::bind_front_handler(&session::on_read, shared_from_this()));}// 读取消息后的回调函数void on_read(beast::error_code ec, std::size_t bytes_transferred) {if (ec == websocket::error::closed) {return;}if (ec) {std::cerr << "read: " << ec.message() << std::endl;return;}std::cout << "Received: " << beast::make_printable(buffer_.data()) << std::endl;buffer_.consume(buffer_.size()); // 清空缓冲区// 回应消息ws_.text(ws_.got_text());ws_.async_write(boost::asio::buffer("Echo: Hello from server"), beast::bind_front_handler(&session::on_write, shared_from_this()));}// 写消息后的回调函数void on_write(beast::error_code ec, std::size_t bytes_transferred) {if (ec) {std::cerr << "write: " << ec.message() << std::endl;return;}// 继续读取消息do_read();}
};// 监听器类
class listener : public std::enable_shared_from_this<listener> {net::io_context& ioc_;tcp::acceptor acceptor_;public:listener(net::io_context& ioc, tcp::endpoint endpoint) : ioc_(ioc), acceptor_(net::make_strand(ioc)) {beast::error_code ec;// 打开接收器acceptor_.open(endpoint.protocol(), ec);if (ec) {std::cerr << "open: " << ec.message() << std::endl;return;}// 绑定到端点acceptor_.bind(endpoint, ec);if (ec) {std::cerr << "bind: " << ec.message() << std::endl;return;}// 开始监听acceptor_.listen(net::socket_base::max_listen_connections, ec);if (ec) {std::cerr << "listen: " << ec.message() << std::endl;return;}}// 启动异步接受操作void run() {do_accept();}private:// 异步接受操作void do_accept() {acceptor_.async_accept(net::make_strand(ioc_), beast::bind_front_handler(&listener::on_accept, shared_from_this()));}// 接受连接后的回调函数void on_accept(beast::error_code ec, tcp::socket socket) {if (ec) {std::cerr << "accept: " << ec.message() << std::endl;} else {// 创建 WebSocket 会话std::make_shared<session>(std::move(socket))->run();}// 继续接受连接do_accept();}
};int main(int argc, char* argv[]) {try {if (argc != 2) {std::cerr << "Usage: websocket-server <port>\n";return EXIT_FAILURE;}// 解析命令行参数auto const port = static_cast<unsigned short>(std::atoi(argv[1]));// 创建 io_contextnet::io_context ioc{1};// 创建并启动监听器std::make_shared<listener>(ioc, tcp::endpoint{tcp::v4(), port})->run();// 运行 io_contextioc.run();} catch (const std::exception& e) {std::cerr << "Error: " << e.what() << std::endl;return EXIT_FAILURE;}return EXIT_SUCCESS;
}

解析

  1. session 类:处理 WebSocket 会话,包括握手、读写消息等。
  2. listener 类:负责监听传入的 TCP 连接,并创建新的 WebSocket 会话。
  3. main 函数:解析命令行参数,创建 io_context,启动监听器并运行。

运行方式: 编译并运行服务器程序,例如:

在 Linux 上

假设将服务器代码保存为 websocket_server.cpp,使用以下命令进行编译和运行:

# 编译服务器代码
g++ -o websocket-server websocket_server.cpp -lboost_system -lboost_thread -lssl -lcrypto -lboost_beast# 运行服务器
./websocket-server 8080
在 Windows 上

假设将服务器代码保存为 websocket_server.cpp,使用以下命令进行编译和运行(需要安装 MinGW):

# 编译服务器代码
g++ -o websocket-server websocket_server.cpp -lboost_system -lws2_32 -lssl -lcrypto# 运行服务器
./websocket-server 8080

 

3. WebSocket 客户端实现
客户端代码
#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <cstdlib>
#include <iostream>
#include <string>namespace beast = boost::beast;         // from <boost/beast.hpp>
namespace websocket = beast::websocket; // from <boost/beast/websocket.hpp>
namespace net = boost::asio;            // from <boost/asio.hpp>
using tcp = net::ip::tcp;               // from <boost/asio/ip/tcp.hpp>int main(int argc, char* argv[]) {try {if (argc != 3) {std::cerr << "Usage: websocket-client <host> <port>\n";return EXIT_FAILURE;}auto const host = argv[1];auto const port = argv[2];// 创建 io_contextnet::io_context ioc;// 解析地址tcp::resolver resolver(ioc);auto const results = resolver.resolve(host, port);// 创建 WebSocket 流websocket::stream<tcp::socket> ws{ioc};// 连接到服务器net::connect(ws.next_layer(), results.begin(), results.end());// 执行 WebSocket 握手ws.handshake(host, "/");// 发送消息ws.write(net::buffer(std::string("Hello from client")));// 读取响应beast::flat_buffer buffer;ws.read(buffer);std::cout << "Received: " << beast::make_printable(buffer.data()) << std::endl;// 关闭 WebSocket 连接ws.close(websocket::close_code::normal);} catch (const std::exception& e) {std::cerr << "Error: " << e.what() << std::endl;return EXIT_FAILURE;}return EXIT_SUCCESS;
}

解析

  1. 解析命令行参数host 和 port
  2. 创建 io_context:管理 I/O 操作。
  3. 解析服务器地址:通过 tcp::resolver
  4. 创建 WebSocket 流:通过 websocket::stream<tcp::socket>
  5. 连接到服务器:通过 net::connect
  6. 执行 WebSocket 握手:通过 ws.handshake
  7. 发送消息:通过 ws.write
  8. 读取响应:通过 ws.read
  9. 关闭 WebSocket 连接:通过 ws.close

运行方式: 编译并运行客户端程序,例如:

在 Linux 上

假设将客户端代码保存为 websocket_client.cpp,使用以下命令进行编译和运行:

# 编译客户端代码
g++ -o websocket-client websocket_client.cpp -lboost_system -lboost_thread -lssl -lcrypto -lboost_beast# 运行客户端
./websocket-client localhost 8080
在 Windows 上

假设将客户端代码保存为 websocket_client.cpp,使用以下命令进行编译和运行(需要安装 MinGW):

# 编译客户端代码
g++ -o websocket-client websocket_client.cpp -lboost_system -lws2_32 -lssl -lcrypto# 运行客户端
./websocket-client localhost 8080

 

运行结果

Received: Echo: Hello from server
五、总结

        本文深入解析了 WebSocket 的基础概念、工作原理及其在 C++ 中的实现,并结合 Boost.AsioBoost.Beast 库实现了 WebSocket 服务器和客户端。通过详细的示例代码和解析,展示了如何在 C++ 中构建高效、实时的双向通信应用。希望本文能帮助读者更好地理解和掌握 WebSocket 技术及其在 C++ 中的应用,提高网络编程技能。

        本文提供的示例代码可以在 Linux 和 Windows 上运行,主要依赖于跨平台的 Boost.AsioBoost.Beast 库。无论您使用哪种操作系统,都需要确保系统上安装了相应的编译器和 Boost 库。通过本文的详细解释和示例,您应该能够在 C++ 环境中实现高效的 WebSocket 客户端和服务器。

这篇关于学懂C++(四十一):网络编程——深入详解 C++ 网络编程之 WebSocket 应用技术的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1096916

相关文章

Java内存分配与JVM参数详解(推荐)

《Java内存分配与JVM参数详解(推荐)》本文详解JVM内存结构与参数调整,涵盖堆分代、元空间、GC选择及优化策略,帮助开发者提升性能、避免内存泄漏,本文给大家介绍Java内存分配与JVM参数详解,... 目录引言JVM内存结构JVM参数概述堆内存分配年轻代与老年代调整堆内存大小调整年轻代与老年代比例元空

从原理到实战深入理解Java 断言assert

《从原理到实战深入理解Java断言assert》本文深入解析Java断言机制,涵盖语法、工作原理、启用方式及与异常的区别,推荐用于开发阶段的条件检查与状态验证,并强调生产环境应使用参数验证工具类替代... 目录深入理解 Java 断言(assert):从原理到实战引言:为什么需要断言?一、断言基础1.1 语

Python中注释使用方法举例详解

《Python中注释使用方法举例详解》在Python编程语言中注释是必不可少的一部分,它有助于提高代码的可读性和维护性,:本文主要介绍Python中注释使用方法的相关资料,需要的朋友可以参考下... 目录一、前言二、什么是注释?示例:三、单行注释语法:以 China编程# 开头,后面的内容为注释内容示例:示例:四

mysql表操作与查询功能详解

《mysql表操作与查询功能详解》本文系统讲解MySQL表操作与查询,涵盖创建、修改、复制表语法,基本查询结构及WHERE、GROUPBY等子句,本文结合实例代码给大家介绍的非常详细,感兴趣的朋友跟随... 目录01.表的操作1.1表操作概览1.2创建表1.3修改表1.4复制表02.基本查询操作2.1 SE

MySQL中的锁机制详解之全局锁,表级锁,行级锁

《MySQL中的锁机制详解之全局锁,表级锁,行级锁》MySQL锁机制通过全局、表级、行级锁控制并发,保障数据一致性与隔离性,全局锁适用于全库备份,表级锁适合读多写少场景,行级锁(InnoDB)实现高并... 目录一、锁机制基础:从并发问题到锁分类1.1 并发访问的三大问题1.2 锁的核心作用1.3 锁粒度分

MySQL数据库中ENUM的用法是什么详解

《MySQL数据库中ENUM的用法是什么详解》ENUM是一个字符串对象,用于指定一组预定义的值,并可在创建表时使用,下面:本文主要介绍MySQL数据库中ENUM的用法是什么的相关资料,文中通过代码... 目录mysql 中 ENUM 的用法一、ENUM 的定义与语法二、ENUM 的特点三、ENUM 的用法1

Python中re模块结合正则表达式的实际应用案例

《Python中re模块结合正则表达式的实际应用案例》Python中的re模块是用于处理正则表达式的强大工具,正则表达式是一种用来匹配字符串的模式,它可以在文本中搜索和匹配特定的字符串模式,这篇文章主... 目录前言re模块常用函数一、查看文本中是否包含 A 或 B 字符串二、替换多个关键词为统一格式三、提

从入门到精通C++11 <chrono> 库特性

《从入门到精通C++11<chrono>库特性》chrono库是C++11中一个非常强大和实用的库,它为时间处理提供了丰富的功能和类型安全的接口,通过本文的介绍,我们了解了chrono库的基本概念... 目录一、引言1.1 为什么需要<chrono>库1.2<chrono>库的基本概念二、时间段(Durat

MySQL count()聚合函数详解

《MySQLcount()聚合函数详解》MySQL中的COUNT()函数,它是SQL中最常用的聚合函数之一,用于计算表中符合特定条件的行数,本文给大家介绍MySQLcount()聚合函数,感兴趣的朋... 目录核心功能语法形式重要特性与行为如何选择使用哪种形式?总结深入剖析一下 mysql 中的 COUNT

C++20管道运算符的实现示例

《C++20管道运算符的实现示例》本文简要介绍C++20管道运算符的使用与实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录标准库的管道运算符使用自己实现类似的管道运算符我们不打算介绍太多,因为它实际属于c++20最为重要的