开源库:jcon-cpp

说明

jcon-cpp 是一个用于 C++ 的 JSON-RPC 库,它允许开发者通过 JSON-RPC 协议进行进程间通信(IPC)。JSON-RPC 是一种轻量级的远程过程调用协议,基于 JSON 格式数据进行通信。基于MIT协议,最新代码基于Qt6实现。可通过Tcp和WebSocket实现RPC。

调整源码以适配Qt5

  • 修改json_rpc_server.cppdoCall函数实现,将QMetaType return_type = QMetaType::fromName(return_type_name)改为int return_type = QMetaType::type(return_type_name)
  • 修改json_rpc_websocket.cppsetupSocket函数实现,将void (QWebSocket::*errorPtr)(QAbstractSocket::SocketError) = &QWebSocket::errorOccurred;修改为void (QWebSocket::*errorPtr)(QAbstractSocket::SocketError) = &QWebSocket::error;

创建基于TCP的RPC服务器

示例代码
jcon::JsonRpcServer* startServer(QObject* parent, SocketType socket_type = SocketType::tcp, bool allow_notifications = false)
{
    jcon::JsonRpcServer* rpc_server;
    if (socket_type == SocketType::tcp) {
        qDebug() << "Starting TCP server";
        rpc_server = new jcon::JsonRpcTcpServer(parent);
    } else {
        qDebug() << "Starting WebSocket server";
        rpc_server = new jcon::JsonRpcWebSocketServer(parent);
    }

    if (allow_notifications)
        rpc_server->enableSendNotification(true);

    auto service1 = new ExampleService;
    auto service2 = new NotificationService;

    rpc_server->registerServices({ service1, service2 });
    rpc_server->listen(6002);
    return rpc_server;
}

auto server = startServer(nullptr, SocketType::tcp);
//do something 
delete server;
说明
  • rpc_server->enableSendNotification(true),用于设置RPC服务器是否可以主动推送消息给客户端

  • ExampleService、NotificationService都是QObject子类,通过元对象系统来定义、存储、调用相关接口,所以接口前需要定义成Q_INVOKABLE

    #pragma once
    #include <QObject>
    
    class ExampleService : public QObject
    {
        Q_OBJECT
    
    public:
        ExampleService();
        virtual ~ExampleService();
    
        Q_INVOKABLE int getRandomInt(int limit);
        Q_INVOKABLE QString printMessage(const QString& msg);
        Q_INVOKABLE void printNotification(const QString& msg);
        Q_INVOKABLE void namedParams(QString& msg, int answer);
    };
    
  • rpc_server->registerServices,将接口服务类注册到RPC服务器中,有两种方式:注册普通服务、注册命名空间服务。

    //注册普通服务
    //m_services结构为QMap<QObject*, QString>,此时key存储的是QObject接口类,value为空
    void JsonRpcServer::registerServices(const QObjectList& services)
    {
        m_services.clear();
    
        for (auto s : services) {
            m_services[s] = "";
        }
        m_ns_separator = "";
    }
    
    //调用
    auto service1 = new ExampleService;
    auto service2 = new NotificationService;
    rpc_server->registerServices({ service1, service2 });
    
    //注册命名空间服务
    //m_services结构为QMap<QObject*, QString>,此时key存储的是QObject接口类,value为对应服务的命名空间字符串
    //ns_separator是一个用于分隔命名空间和方法名的字符串,默认为 /
    void JsonRpcServer::registerServices(const ServiceMap& services,
                                         const QString& ns_separator)
    {
        m_services = services;
        m_ns_separator = ns_separator;
    }
    
    //调用
    auto service1 = new ExampleService;
    auto service2 = new OtherService;
    auto service3 = new NotificationService;
    rpc_server->registerServices(
    {
    	{ service1, "ex/myFirstNamespace" },
    	{ service2, "ex/myOtherNamespace" },
    	{ service3, "ex/myNotificationNamespace" }
    }, "/");
    
  • rpc_server->listen(6002)监听某端口,JsonRpcServer有两个子类:JsonRpcTcpServer、JsonRpcWebSocketServerJsonRpcTcpServer实际就是实现了一个QTcpServer监听端口、客户端连接时创建Socket对象、Socket有请求时通过元对象系统找到对应的函数执行并返回结果

    /*class JsonRpcTcpServer*/
    
    //1.JsonRpcTcpServer构造函数,处理客户端连接
    m_server.connect(&m_server, &QTcpServer::newConnection, this, &JsonRpcTcpServer::newConnection);
    
    //2.JsonRpcTcpServer::newConnection,创建相关通信对象
    QTcpSocket* tcp_socket = m_server.nextPendingConnection();
    auto rpc_socket = std::make_shared<JsonRpcTcpSocket>(tcp_socket);
    auto endpoint = std::shared_ptr<JsonRpcEndpoint>(new JsonRpcEndpoint(rpc_socket, log(), this), [this](JsonRpcEndpoint* obj) {
    	if (this->m_server.isListening()) {
    		obj->deleteLater();
    	} else {
    		delete obj;
    	}
    });
    connect(endpoint.get(), &JsonRpcEndpoint::jsonObjectReceived, this, &JsonRpcServer::jsonRequestReceived);
    
    //3.JsonRpcServer::jsonRequestReceived,处理客户端请求
    //request是请求数据,json格式;socket是对应的通信对象
    void JsonRpcServer::jsonRequestReceived(const QJsonObject& request, QObject* socket)
    {
    	//校验json的key:jsonrpc
    	if (request.value("jsonrpc").toString() != "2.0") {
            logError("invalid protocol tag");
            return;
    	}
    	//获取json的key:method
    	QString method_name = request.value("method").toString();
    	//获取json的key:params
    	QVariant params = request.value("params").toVariant();
    	//获取json的key:id
    	QString request_id = request.value("id").toVariant().toString();
    	
    	//从存储的元对象中找到对应的函数并执行
    	dispatch(method_name, params, return_value)
    }
    

    创建基于TCP的RPC客户端

    示例代码
    jcon::JsonRpcClient* startClient(QObject* parent,
                                     SocketType socket_type = SocketType::tcp,
                                     bool allow_notifications = false)
    {
        jcon::JsonRpcClient* rpc_client;
        if (socket_type == SocketType::tcp) {
            rpc_client = new jcon::JsonRpcTcpClient(parent);
            rpc_client->connectToServer("127.0.0.1", 6002);
        } else {
            rpc_client = new jcon::JsonRpcWebSocketClient(parent);
            // This is just to illustrate the fact that connectToServer also accepts
            // a QUrl argument.
            rpc_client->connectToServer(QUrl("ws://127.0.0.1:6002"));
        }
    
        if (allow_notifications)
            rpc_client->enableReceiveNotification(true);
    
        return rpc_client;
    }
    
    auto rpc_client = startClient(&app, SocketType::tcp);
    
    说明
    • 调用通知类接口,无返回,通过函数名调用

      rpc_client->notification("printNotification", "hello, notification");

    • 异步调用接口,有返回,通过函数名调用

      auto req = rpc_client->callAsync("getRandomInt", 10);

      req->connect(req.get(), &jcon::JsonRpcRequest::result,
      [](const QVariant& result) {
      qDebug() << "result of asynchronous RPC call:" << result;
      });

      req->connect(req.get(), &jcon::JsonRpcRequest::error,
      [](int code, const QString& message) {
      qDebug() << "RPC error:" << message
      << " (" << code << ")";
      });

    • 同步调用接口,有返回,通过函数名调用

      auto result = rpc_client->call("getRandomInt", 100);

      if (result->isSuccess()) {
      qDebug() << "result of synchronous RPC call:" << result->result();
      } else {
      qDebug() << "RPC error:" << result->toString();
      }

      //call里通过事件循环等待完成

    • 异步调用接口,参数命名化方式,有返回,通过函数名调用

      //将参数存入QVariantMap,参数1是msg,参数2是answer
      auto req = rpc_client->callAsyncNamedParams("namedParams",
      QVariantMap{
      {"msg", "hello, world"},
      {"answer", 42}
      });

      req->connect(req.get(), &jcon::JsonRpcRequest::result,
      [](const QVariant& result) {
      qDebug() << "result of asynchronous RPC call:" << result;
      });

      req->connect(req.get(), &jcon::JsonRpcRequest::error,
      [](int code, const QString& message) {
      qDebug() << "RPC error:" << message
      << " (" << code << ")";
      });

相关推荐
Merlin-Ice4 分钟前
C#Struct堆栈
开发语言·c#
飞的肖12 分钟前
前端全局水印, 拖拉拽图片 ,拽入等比压缩,上传服务器后,java 转base64 加水印,然后前端http预览,确认保存,拽出删除。
java·开发语言·前端·图片上传
java熊猫17 分钟前
Erlang语言的软件工程
开发语言·后端·golang
Good Note17 分钟前
Golang笔记——rune和byte
开发语言·笔记·golang
Linux520小飞鱼23 分钟前
Perl语言的网络编程
开发语言·后端·golang
玩电脑的辣条哥28 分钟前
怎么用python写个唤醒睡眠电脑的脚本?
开发语言·python·电脑
周振超的41 分钟前
使用Qt和OpenGL实现一个旋转的各面颜色不一致的立方体及知识点分析
开发语言·qt·着色器
Kylin5242 小时前
Java异常处理
java·开发语言·jvm
爱上语文4 小时前
Http请求响应——响应
java·开发语言·网络协议·http
lzb_kkk4 小时前
【C++】C++11异步操作
c语言·开发语言·c++·1024程序员节