cyanbreak
cyanbreak

Reputation: 101

How to write a gRPC client/server in C?

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.

Upvotes: 9

Views: 14607

Answers (3)

Spade 000
Spade 000

Reputation: 109

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;
}

Upvotes: 0

Tabish Mir
Tabish Mir

Reputation: 799

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.

Upvotes: 1

Yash Tibrewal
Yash Tibrewal

Reputation: 437

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.

Upvotes: 5

Related Questions