You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C++中不依赖推荐GraphQL库,用WebSocket实现GraphQL订阅?

在C++中实现GraphQL订阅(无需指定GraphQL库)

curlpp是HTTP客户端,仅支持短连接的请求-响应模式,而GraphQL订阅依赖双向长连接,必须通过WebSocket实现(和你提到的Apollo方案一致)。下面是具体实现步骤和代码示例:

核心思路

GraphQL订阅通常遵循graphql-ws协议,流程为:

  1. 建立WebSocket连接到服务器的WebSocket端点
  2. 发送初始化消息(携带认证token)
  3. 发送订阅请求(包含GraphQL查询)
  4. 持续监听服务器推送的更新
  5. 处理连接关闭或错误

选择WebSocket库

推荐使用Boost.Beast(C++17兼容,提供完整的WebSocket实现),也可以选择libwebsockets或uWebSockets。以下示例基于Boost.Beast。

代码实现

基于你现有的TestClient类,新增WebSocket相关逻辑:

1. 更新头文件(TestClient.hpp)

#include <string>
#include <list>
#include <curlpp/Easy.hpp>
#include "jsoncpp/json/reader.h"
#include <boost/beast.hpp>
#include <boost/asio.hpp>
#include <nlohmann/json.hpp>

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

class TestClient {
public:
    TestClient();
    virtual ~TestClient();

    void login();
    void subscribe();

protected:
    std::string sendRequest(const std::list<std::string> &header, const std::string &graphQL);
    std::string loadFile(std::string filePath);

private:
    curlpp::Easy *m_request = new curlpp::Easy();
    Json::Reader m_reader;
    std::string m_token = "";
    std::string m_grapqlLogin;
    std::string m_grapqlSubscribe;

    // HTTP和WebSocket配置
    const std::string m_HTTP_URL = "http://localhost:4000/graphiql";
    const std::string m_WS_HOST = "localhost";
    const uint16_t m_WS_PORT = 4000;
    const std::string m_WS_TARGET = "/graphql"; // WebSocket端点,注意不是/graphiql
    const std::string m_headerContentType = "Content-Type: application/json";
    const std::string m_headerToken = "Authorization:Bearer ";

    // WebSocket订阅逻辑
    void runWebSocketSubscription();
};

2. 更新实现文件(TestClient.cpp)

保留原有的login()和sendRequest()方法,修改subscribe()并新增WebSocket处理逻辑:

void TestClient::subscribe()
{
    if (m_token.empty()) {
        std::cerr << "错误:请先登录获取token!" << std::endl;
        return;
    }
    runWebSocketSubscription();
}

void TestClient::runWebSocketSubscription()
{
    try {
        // 初始化IO上下文
        net::io_context ioc;

        // 解析服务器地址
        tcp::resolver resolver(ioc);
        auto const results = resolver.resolve(m_WS_HOST, std::to_string(m_WS_PORT));

        // 创建WebSocket流并连接
        websocket::stream<tcp::socket> ws(ioc);
        beast::get_lowest_layer(ws).connect(results);

        // 握手时携带认证token
        ws.set_option(websocket::stream_base::decorator(
            [this](websocket::request_type& req)
            {
                req.set(beast::http::field::authorization, m_headerToken + m_token);
                req.set(beast::http::field::user_agent, BOOST_BEAST_VERSION_STRING);
            }));
        ws.handshake(m_WS_HOST, m_WS_TARGET);

        // 1. 发送连接初始化消息(graphql-ws协议要求)
        json init_msg = {
            {"type", "connection_init"},
            {"payload", {{"Authorization", m_headerToken + m_token}}}
        };
        ws.write(net::buffer(init_msg.dump()));

        // 接收服务器的连接确认
        beast::flat_buffer buffer;
        ws.read(buffer);
        std::cout << "连接初始化响应:" << beast::buffers_to_string(buffer.data()) << std::endl;
        buffer.consume(buffer.size());

        // 2. 发送订阅请求
        json subscribe_msg = {
            {"id", "sub_1"}, // 唯一标识该订阅
            {"type", "start"},
            {"payload", {
                {"query", m_grapqlSubscribe},
                {"variables", {}} // 如有订阅变量可在此添加
            }}
        };
        ws.write(net::buffer(subscribe_msg.dump()));

        // 3. 持续监听服务器推送的更新
        std::cout << "开始监听订阅更新..." << std::endl;
        while (true) {
            ws.read(buffer);
            std::string update_str = beast::buffers_to_string(buffer.data());
            json update_json = json::parse(update_str);
            
            std::cout << "\n收到更新:" << update_json.dump(4) << std::endl;
            buffer.consume(buffer.size());

            // 处理停止信号(可选)
            if (update_json["type"] == "stop") {
                std::cout << "订阅已停止" << std::endl;
                break;
            }
        }

        // 关闭连接
        ws.close(websocket::close_code::normal);
    } catch (const std::exception& e) {
        std::cerr << "WebSocket错误:" << e.what() << std::endl;
    }
}

关键注意事项

  • 端点差异:HTTP请求用/graphiql,但WebSocket订阅必须连接到服务器的GraphQL WebSocket端点(通常是/graphql),请确认服务器配置。
  • 协议规范:howtographql的服务器使用graphql-ws协议,消息类型必须严格符合规范(connection_init、start、data等)。
  • JSON库:示例用nlohmann/json处理JSON,你可以替换为熟悉的JsonCpp,只需调整序列化/反序列化逻辑。
  • 异步处理:示例是阻塞式监听,若需要非阻塞或多线程,可结合Boost.Asio的异步API实现。

为什么curlpp无法处理订阅

curlpp仅支持HTTP短连接的请求-响应模式,无法维持长连接进行双向实时通信,而GraphQL订阅依赖WebSocket的长连接特性,因此必须使用WebSocket库替代curlpp处理订阅逻辑。

内容的提问来源于stack exchange,提问作者PhiloLars

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 02:54:30