📜 ⬆️ ⬇️

Use Thrift in C

Greetings, dear habraiser.

I'll start with a little background. At the moment I am working on a rather complex product, which consists of the server part (includes several services) and the client SDK ported to certain platforms. All this zoo we need to somehow test together.

Client applications (some kind of drivers) were made that use the corresponding SDKs and are able to receive commands from the testing service of the “go to the server, do this” type, or “give me such a result for testing”. The client receives the commands using Thrift ( wiki ). And everything was fine, until we got to porting the SDK to C and did not find that Apache didn’t have an intelligent manual for C. Found a ticket for the creation of this manual and a poor example there. After the successful application of Thrift in C, it was decided to write a small educational program on this topic.
')
The goals we set are:
- The client should receive commands from the testing service using Thrift;
- Commands are great, but you also need to communicate with the server;
- The client must work in the same thread.

I will describe the features of work, using the example of “RPC calculator” from the Apache website. Thrift's n-th implementation uses glib, which means we'll have to.
First of all, we will generate the sources according to the scheme from the example:

thrift -r --gen c_glib ./tutorial.thrift

Received several * .c and * .h files. We will need them in the future.
Now we will write a function to initialize the Thrift connection:

Thrift_init () function
 #include <glib.h> #include <glib-object.h> #include <thrift/c_glib/protocol/thrift_protocol.h> #include <thrift/c_glib/protocol/thrift_binary_protocol.h> #include <thrift/c_glib/transport/thrift_transport.h> #include <thrift/c_glib/transport/thrift_buffered_transport.h> #include <thrift/c_glib/transport/thrift_socket.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <calculator.h> //   static ThriftSocket *socket = NULL; static ThriftTransport *transport = NULL; static ThriftProtocol *protocol = NULL; static CalculatorClient *client = NULL; static CalculatorIf *iface = NULL; int thrift_init(const char *hostname, int port) { printf("Initializing Thrift client (server=%s:%d)...\n", hostname, port); g_type_init(); GError *error = NULL; socket = THRIFT_SOCKET(g_object_new(THRIFT_TYPE_SOCKET, "hostname", hostname, "port", port, &error)); if (error) { printf("Failed to create thrift socket: %s\n", error->message); g_error_free(error); return -1; } transport = THRIFT_TRANSPORT(g_object_new(THRIFT_TYPE_BUFFERED_TRANSPORT, "transport", socket, &error)); if (error) { printf("Failed to create thrift transport: %s\n", error->message); g_error_free(error); return -1; } protocol = THRIFT_PROTOCOL(g_object_new(THRIFT_TYPE_BINARY_PROTOCOL, "transport", transport, &error)); if (error) { printf("Failed to create thrift binary protocol: %s\n", error->message); g_error_free(error); return -1; } client = CALCULATOR_CLIENT(g_object_new(TYPE_CALCULATOR_CLIENT, "input_protocol", protocol, "output_protocol", protocol, &error)); if (error) { printf("Failed to create thrift client: %s\n", error->message); g_error_free(error); return -1; } iface = CALCULATOR_IF(client); thrift_transport_open(transport, &error); //   if (error) { printf("Failed to open thrift connection: %s\n", error->message); g_error_free(error); return -1; } return socket->sd; //   .   } 


Here we create and initialize such important things as: a socket for data transfer, a transport binary protocol, a client interface, etc. Now, let's try calling the calculator method:

 GError *error = NULL; gint32 result = 0; calculator_client_add(iface, &result, 4, 3, &error); 


Well, the result variable got the value 7. But that's not enough for us. The call turned out to be blocking, but in our case the testing service may not immediately return the command / result, while the client still needs to read and process the responses from our server (working in one thread, remember?). Fortunately, the Thrift implementation allows us to split the call to calculator_client_add into 2 calls: calculator_client_send_add and calculator_client_recv_add . The first of them sends a request with function arguments. The second, respectively, reads the response with the returned value. Our thrift_init function just returns the socket Thrift handle, and we will use it:

 int main() { int socket = -1; if ((socket = thrift_init("localhost", 9090)) >= 0) { GError *error = NULL; calculator_client_send_add(iface, 4, 3, &error); if (error) { //   g_error_free(error); return -1; } struct pollfd fds; fds.fd = socket; fds.events = POLLIN; fds.revents = 0; if (poll(&fds, 1, 5000 /*5  */) > 0) { if (fds.revents & POLLIN) { gint32 result = 0; calculator_client_recv_add(iface, &result, &error); //  7  result if (error) { //   g_error_free(error); return -1; } } } return 0; } return -1; } 


And finally - a function for freeing resources:

Thrift_destroy () function
 void thrift_destroy() { if (transport) { thrift_transport_close(transport, NULL); g_object_unref(transport); } if (client) { g_object_unref(client); } if (protocol) { g_object_unref(protocol); } if (socket) { g_object_unref(socket); } } 

Thus, we managed to kill two birds with one stone: chat with the server and pull thrift calls. In this case, no fork'ov and thread'ov. I want to add that Thrift is a great and easy-to-use RPC tool ... Even if you are developing in C.

Source: https://habr.com/ru/post/236345/


All Articles