Using Thrift in C
Greetings, dear habrayuzer.
I'll start with a little background. At the moment, I am working on a rather complex product, which consists of a server part (includes several services) and a client SDK ported to certain platforms. We need to test this whole zoo together somehow.
Client applications (some kind of drivers) were made that use the appropriate SDKs and are able to receive commands from a testing service such as "go to the server, do this", or "give me such and such a result for verification." The client receives commands using the Thrift ( wiki ). And everything was fine until we got to porting the SDK to C and found that Apache had no sensible manual for C. Found a ticketto create this manual and a meager example in the same place. After the successful use of Thrift in C, it was decided to write a small educational program on this topic.
The goals that we set:
- The client should receive commands from the testing service using Thrift;
- Teams are great, but you also need to communicate with the server;
- The client must work in one thread.
I will describe the features of work using the example of “RPC calculator” from the Apache website . Thrift's nice implementation uses glib, which means we'll have to.
First of all, we will generate the sources according to the scheme from the example:
We got several * .c and * .h files. We will need them in the future.
Now we will write a function to initialize the Thrift connection:
Here we create and initialize such important things as: a socket for data transfer, a transport binary protocol, a client interface, etc. Now we try to call the calculator method:
Well, we got the value 7 in the result variable. But this is not enough for us. The call turned out to be blocking, and 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 one sends a request with function arguments. The second, respectively, reads the response with the returned value. Our thrift_init function just returns the Thrift handle of the socket, and we will use it:
And finally - a function to free resources:
Thus, we managed to kill two birds with one stone: to communicate with the server, and to pull the thrift calls. In this case, no forks and threads. I want to add that Thrift is a wonderful and easy-to-use RPC tool ... Even if you are developing in C.
I'll start with a little background. At the moment, I am working on a rather complex product, which consists of a server part (includes several services) and a client SDK ported to certain platforms. We need to test this whole zoo together somehow.
Client applications (some kind of drivers) were made that use the appropriate SDKs and are able to receive commands from a testing service such as "go to the server, do this", or "give me such and such a result for verification." The client receives commands using the Thrift ( wiki ). And everything was fine until we got to porting the SDK to C and found that Apache had no sensible manual for C. Found a ticketto create this manual and a meager example in the same place. After the successful use of Thrift in C, it was decided to write a small educational program on this topic.
The goals that we set:
- The client should receive commands from the testing service using Thrift;
- Teams are great, but you also need to communicate with the server;
- The client must work in one thread.
I will describe the features of work using the example of “RPC calculator” from the Apache website . Thrift's nice 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
We got 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include // Сгенеренный хидер
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 we try to call the calculator method:
GError *error = NULL;
gint32 result = 0;
calculator_client_add(iface, &result, 4, 3, &error);
Well, we got the value 7 in the result variable. But this is not enough for us. The call turned out to be blocking, and 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 one sends a request with function arguments. The second, respectively, reads the response with the returned value. Our thrift_init function just returns the Thrift handle of the socket, 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 to free 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: to communicate with the server, and to pull the thrift calls. In this case, no forks and threads. I want to add that Thrift is a wonderful and easy-to-use RPC tool ... Even if you are developing in C.