How to write a gRPC client/server in C?
Asked Answered
V

3

9

I have a program written in C and want to include gRPC in it. However, the API for gRPC is written in C++.

I've looked here and got the foo_client and foo_server working. https://github.com/Juniper/grpc-c/tree/master/examples

However, the C client is not compatible with my gRPC C++ server. They will not talk to each other. I believe it is because I am using the lates gRPC which uses protocbuf version 3.2.0. And Juniper's grpc-c is using an older version of gRPC that uses protocbuf version 3.0.0.

So the Juniper version in C doesn't seem to work with the new gRPC. I know the gRPC low level C API is supposed to be here: https://github.com/grpc/grpc/blob/master/include/grpc/grpc.h But I'm having difficulty implementing it. Can anyone help me make sense of it?

I haven't programmed in C in awhile so I'm a little rusty.

Vacuum answered 20/6, 2018 at 17:11 Comment(0)
C
5

If you are using the gRPC core library directly, then you will need to perform your own serialization, and deal with low level operations documented in https://github.com/grpc/grpc/blob/master/include/grpc/impl/codegen/grpc_types.h.

If you have any specific questions, we will be happy to help, but if this is just a one-time thing it might be easier to just solve the version incompatibility problem, or maybe simply wrap the C++ implementation with a C interface.

Chloechloette answered 20/6, 2018 at 18:46 Comment(3)
Do you know of any code examples for C wrapper for C++ implementation ?Biggin
@Biggin extern C {}, see https://mcmap.net/q/600355/-c-wrapper-for-c?Lemmy
Is there a cleaner way to do this in 2022?Helbonia
G
0

There are a few ways to approach this:

  1. Call gRPC's CPP functions from your C code: To do so, create a C API to expose the features of your C++ code. Write C++ functions marked with extern "C" and design a pure C API that encapsulates the C++ library. More details here: How to call C++ function from C? and here: Mixing C and Cpp
  2. Use unofficial gRPC libraries for C: I have seen juniper-grpc-c (Github) mentioned in a few different places.
  3. Use a HTTP/2 C Library: gRPC builds on top of HTTP2, so with a library like https://nghttp2.org/, you could have C bindings for gRPC.
  4. Use Apache Thrift: Thrift is a lightweight, language-independent software stack for point-to-point RPC implementation which has a C-client. If replacing gRPC is an option, you can try this out.
Galluses answered 11/10, 2023 at 15:42 Comment(0)
P
0

There's an EZgRPC server written in C, but there's some limitations, like:

  1. SSL aren't (currently) implemented.
  2. The server (currently) doesn't support streaming messages.
  3. A client EZgRPC aren't implemented. Only the server.
  4. The user will have to deal with the serialization of grpc message when using other message format (like protobuf). Though, if they like it raw, is possible.
  5. Only works in a linux based OS.

EZgRPC: https://github.com/mnyoshie/ezgrpc

Taken from hello_world_server.c:

/* the author disclaims copyright to this example source code
 * and releases it into the public domain
 */

#include "ezgrpc.h"

int whatever_service1(ezgrpc_message_t *req, ezgrpc_message_t **res, void *userdata){
  ezgrpc_message_t *msg = calloc(1, sizeof(ezgrpc_message_t));
  printf("called service1. received %u bytes\n", req->data_len);

  msg->is_compressed = 0;
  msg->data_len = 3;
  msg->data = malloc(3);
  /* protobuf serialized message */
  msg->data[0] = 0x08;
  msg->data[1] = 0x96;
  msg->data[2] = 0x02;
  msg->next = NULL;

  *res = msg;
  //sleep(2);
  return 0;
}

int another_service2(ezgrpc_message_t *req, ezgrpc_message_t **res, void *userdata){
  printf("called service2\n");
  return 0;
}

int main(){

  int pfd[2];
  if (pipe(pfd))
    assert(0);
  
  sigset_t sig_mask;
  ezhandler_arg ezarg = {&sig_mask, pfd[1]};
  if (ezgrpc_init(ezserver_signal_handler, &ezarg)) {
    fprintf(stderr, "fatal: couldn't init ezgrpc\n");
    return 1;
  }

  EZGRPCServer *server_handle = ezgrpc_server_init();
  assert(server_handle != NULL);

  ezgrpc_server_add_service(server_handle, "/test.yourAPI/whatever_service1", whatever_service1, NULL, NULL, 0);
  ezgrpc_server_add_service(server_handle, "/test.yourAPI/another_service2", another_service2, NULL, NULL, 0);

  ezgrpc_server_set_ipv4_bind_addr(server_handle, "0.0.0.0");
  ezgrpc_server_set_ipv4_bind_port(server_handle, 19009);

  ezgrpc_server_set_ipv6_bind_addr(server_handle, "::");
  ezgrpc_server_set_ipv6_bind_port(server_handle, 19009);
  ezgrpc_server_set_shutdownfd(server_handle, pfd[0]);

  /* This call is blocking.
   * when a SIGINT/SIGTERM is received, it should return */
  ezgrpc_server_start(server_handle);

  ezgrpc_server_free(server_handle);

  return 0;
}
Pelletier answered 24/4 at 4:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.