如何使用 grpc 和 google 协议缓冲区对数组进行就地修改?

How do I make an in-place modification on an array using grpc and google protocol buffers?

我在使用 grpc 的 google 协议缓冲区的 const 请求时遇到问题。这是我的问题:

我想就地修改数组的值。为此,我写了这个简单的例子,我尝试传递一个数组并对它的所有内容求和。这是我的代码:

adder.proto:

syntax = "proto3";

option java_package = "io.grpc.examples";

package adder;

// The greeter service definition.
service Adder {
    // Sends a greeting
    rpc Add (AdderRequest) returns (AdderReply) {}
}

// The request message containing the user's name.
message AdderRequest {
    repeated int32 values = 1;
}

// The response message containing the greetings
message AdderReply {
    int32 sum = 1;
}

server.cc:

//
// Created by Eric Reis on 7/6/16.
//

#include <iostream>
#include <grpc++/grpc++.h>

#include "adder.grpc.pb.h"

class AdderImpl final : public adder::Adder::Service
{
public:
    grpc::Status Add(grpc::ServerContext* context, const adder::AdderRequest* request,
                     adder::AdderReply* reply) override
    {
        int sum = 0;
        for(int i = 0, sz = request->values_size(); i < sz; i++)
        {
            request->set_values(i, 10); // -> this gives an error caused by the const declaration of the request variable
                                        //    error: "Non-const function 'set_values' is called on the const object"
            sum += request->values(i);  // -> this works fine
        }
        reply->set_sum(sum);
        return grpc::Status::OK;
    }
};

void RunServer()
{
    std::string server_address("0.0.0.0:50051");
    AdderImpl service;

    grpc::ServerBuilder builder;
    // Listen on the given address without any authentication mechanism.
    builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
    // Register "service" as the instance through which we'll communicate with
    // clients. In this case it corresponds to an *synchronous* service.
    builder.RegisterService(&service);
    // Finally assemble the server.
    std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
    std::cout << "Server listening on " << server_address << std::endl;

    // Wait for the server to shutdown. Note that some other thread must be
    // responsible for shutting down the server for this call to ever return.
    server->Wait();
}

int main(int argc, char** argv)
{
    RunServer();
    return 0;
}

client.cc:

//
// Created by Eric Reis on 7/6/16.
//

#include <iostream>
#include <grpc++/grpc++.h>

#include "adder.grpc.pb.h"

class AdderClient
{
public:
    AdderClient(std::shared_ptr<grpc::Channel> channel) : stub_(adder::Adder::NewStub(channel)) {}

    int Add(int* values, int sz) {
        // Data we are sending to the server.
        adder::AdderRequest request;
        for (int i = 0; i < sz; i++)
        {
            request.add_values(values[i]);
        }

        // Container for the data we expect from the server.
        adder::AdderReply reply;

        // Context for the client. It could be used to convey extra information to
        // the server and/or tweak certain RPC behaviors.
        grpc::ClientContext context;

        // The actual RPC.
        grpc::Status status = stub_->Add(&context, request, &reply);

        // Act upon its status.
        if (status.ok())
        {
            return reply.sum();
        }
        else {
            std::cout << "RPC failed" << std::endl;
            return -1;
        }
    }

private:
    std::unique_ptr<adder::Adder::Stub> stub_;
};

int main(int argc, char** argv) {
    // Instantiate the client. It requires a channel, out of which the actual RPCs
    // are created. This channel models a connection to an endpoint (in this case,
    // localhost at port 50051). We indicate that the channel isn't authenticated
    // (use of InsecureChannelCredentials()).
    AdderClient adder(grpc::CreateChannel("localhost:50051",
                                          grpc::InsecureChannelCredentials()));
    int values[] = {1,2};
    int sum = adder.Add(values, 2);
    std::cout << "Adder received: " << sum << std::endl;

    return 0;
}

当我尝试在定义为 const 的请求对象上调用方法 set_values() 时,我的错误发生了。我理解为什么会发生此错误,但我只是想不出一种方法来克服它而不复制数组。

我试图删除 const 定义,但是当我这样做时 RPC 调用失败。

由于我是这个 RPC 世界的新手,而且我对 grpc 和 google 协议缓冲区更陌生,所以我想寻求您的帮助。解决这个问题的最佳方法是什么?

请看我的回答here。服务器收到客户端发送的 AdderRequest 的副本。如果你要修改它,客户端原来的AdderRequest不会被修改。如果 "in place" 你的意思是服务器修改了客户端的原始内存,那么没有任何 RPC 技术可以真正做到这一点,因为客户端和服务器 运行 在不同的地址空间(进程)中,甚至在不同的机器上。

如果确实需要服务端修改客户端内存:

  1. 确保服务器和客户端运行在同一台机器上。
  2. 使用 OS 特定的共享内存 API,例如 shm_open() and mmap() 将相同的物理内存块映射到客户端和服务器的地址空间。
  3. 使用RPC传输共享内存的标识符(名称)(不是内存中的实际数据)并调用服务器的处理。
  4. 当客户端和服务器都打开并映射内存时,它们都有指向相同物理内存的指针(可能在不同的地址空间中具有不同的值),因此服务器将能够读取客户端在那里写入的内容(无需复制或传输),反之亦然。