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

目录

一、引言

[二、WebSocket 概念](#二、WebSocket 概念)

[1. WebSocket 概述](#1. WebSocket 概述)

[2. WebSocket 协议](#2. WebSocket 协议)

[WebSocket 握手请求示例](#WebSocket 握手请求示例)

[三、WebSocket 工作原理](#三、WebSocket 工作原理)

[四、WebSocket 的实现方式](#四、WebSocket 的实现方式)

[1. 准备工作](#1. 准备工作)

[2. WebSocket 服务器实现](#2. WebSocket 服务器实现)

服务器代码

[3. WebSocket 客户端实现](#3. WebSocket 客户端实现)

客户端代码

五、总结


一、引言

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

二、WebSocket 概念
1. WebSocket 概述

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

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

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

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

WebSocket 握手响应示例

cpp 复制代码
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 库:

bash 复制代码
sudo apt-get update sudo apt-get install libboost-all-dev
在 Windows 上

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

  1. 下载并安装 vcpkg
  2. 使用 vcpkg 安装 Boost 库:
bash 复制代码
vcpkg install boost-asio boost-beast
2. WebSocket 服务器实现
服务器代码
cpp 复制代码
#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_context
        net::io_context ioc{1};

        // 创建并启动监听器
        std::make_shared<listener>(ioc, tcp::endpoint{tcp::v4(), port})->run();

        // 运行 io_context
        ioc.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,使用以下命令进行编译和运行:

cpp 复制代码
# 编译服务器代码
g++ -o websocket-server websocket_server.cpp -lboost_system -lboost_thread -lssl -lcrypto -lboost_beast

# 运行服务器
./websocket-server 8080
在 Windows 上

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

cpp 复制代码
# 编译服务器代码
g++ -o websocket-server websocket_server.cpp -lboost_system -lws2_32 -lssl -lcrypto

# 运行服务器
./websocket-server 8080
3. WebSocket 客户端实现
客户端代码
cpp 复制代码
#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_context
        net::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. 解析命令行参数hostport
  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,使用以下命令进行编译和运行:

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):

cpp 复制代码
# 编译客户端代码
g++ -o websocket-client websocket_client.cpp -lboost_system -lws2_32 -lssl -lcrypto

# 运行客户端
./websocket-client localhost 8080

运行结果

cpp 复制代码
Received: Echo: Hello from server
五、总结

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

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

相关推荐
爱码小白24 分钟前
网络编程(王铭东老师)笔记
服务器·网络·笔记
CYBEREXP200826 分钟前
MacOS M3源代码编译Qt6.8.1
c++·qt·macos
蜜獾云34 分钟前
linux firewalld 命令详解
linux·运维·服务器·网络·windows·网络安全·firewalld
柒烨带你飞1 小时前
路由器转发数据报的封装过程
网络·智能路由器
yuanbenshidiaos1 小时前
c++------------------函数
开发语言·c++
yuanbenshidiaos1 小时前
C++----------函数的调用机制
java·c++·算法
tianmu_sama1 小时前
[Effective C++]条款38-39 复合和private继承
开发语言·c++
羚羊角uou2 小时前
【C++】优先级队列以及仿函数
开发语言·c++
东方隐侠安全团队-千里2 小时前
网安瞭望台第17期:Rockstar 2FA 故障催生 FlowerStorm 钓鱼即服务扩张现象剖析
网络·chrome·web安全
姚先生972 小时前
LeetCode 54. 螺旋矩阵 (C++实现)
c++·leetcode·矩阵