Published on

Using Apache Thrift 3: Running a Sample Program on Visual C++

Authors

This time let's build a simple sample. One of Thrift's main features is that it enables RPC across different programming languages, but this time both the client and server will be simple C++ programs that perform the four basic arithmetic operations. The procedure is as follows.

  1. Define the thrift file and generate the client and server code
  2. Implement the server
  3. Implement the client

1. Define the thrift file and generate the client/server code

Write the thrift file in IDL, then compile it with the compiler built in the previous post. Thrift will automatically generate the communication layer for the client and server. First create a file named Calculator.thrift and write it as follows.

namespace cpp calc

service Calculator {
  double plus(1: double arg1, 2: double arg2),
  double minus(1: double arg1, 2: double arg2),
  double multiplies(1: double arg1, 2: double arg2),
  double divides(1: double arg1, 2: double arg2),
}

From the command line:

$ thrift.exe -gen cpp Calculator.thrift

This creates the gen-cpp folder. For the detailed syntax, refer to the official documentation.

2. Implement the server

The server skeleton is generated automatically in gen-cpp/Calculator_server.skelton.cpp, so copy it and implement the service section like this. Do not forget to link libthrift.lib.

#define HAVE_CONFIG_H

#include "gen-cpp/Calculator.h"
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/server/TSimpleServer.h>
#include <thrift/transport/TServerSocket.h>
#include <thrift/transport/TBufferTransports.h>

using namespace apache::thrift;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;
using namespace apache::thrift::server;
using namespace apache::thrift::concurrency;

using boost::shared_ptr;

using namespace ::calc;

class CalculatorHandler : virtual public CalculatorIf {
public:
  CalculatorHandler() {
    // Your initialization goes here
  }

  double plus(const double arg1, const double arg2) {
    // Your implementation goes here
    printf("plusn");
    return arg1 + arg2;
  }

  double minus(const double arg1, const double arg2) {
    // Your implementation goes here
    printf("minusn");
    return arg1 - arg2;
  }

  double multiplies(const double arg1, const double arg2) {
    // Your implementation goes here
    printf("multipliesn");
    return arg1 * arg2;
  }

  double divides(const double arg1, const double arg2) {
    // Your implementation goes here
    printf("dividesn");
    return arg1 / arg2;
  }

};

int main(int argc, char **argv) {

  WSADATA wsaData;
  WSAStartup(MAKEWORD(2, 0), &wsaData);

  int port = 9090;
  shared_ptr handler(new CalculatorHandler());
  shared_ptr processor(new CalculatorProcessor(handler));
  shared_ptr serverTransport(new TServerSocket(port));
  shared_ptr transportFactory(new TBufferedTransportFactory());
  shared_ptr protocolFactory(new TBinaryProtocolFactory());

  TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);
  server.serve();
  return 0;
}

The important point on Windows is that if you do not write the following two lines, you get the runtime error shown below.

WSADATA wsaData;
WSAStartup(MAKEWORD(2, 0), &wsaData);

TServerSocket::listen() socketpair() errno = 10093 getaddrinfo 10093: The application has not called WSAStartup, or WSAStartup failed.

3. Implement the client

Finally, implement the client.

#define HAVE_CONFIG_H

#include "gen-cpp/Calculator.h"

#include <protocol/TBinaryProtocol.h>
#include <transport/TSocket.h>
#include <transport/TTransportUtils.h>

using namespace apache::thrift;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;

using namespace calc;

int main(int argc, char** argv) {

  boost::shared_ptr socket(new TSocket("localhost", 9090));
  boost::shared_ptr transport(new TBufferedTransport(socket));
  boost::shared_ptr protocol(new TBinaryProtocol(transport));

  CalculatorClient client(protocol);

  try {
    transport->open();

    printf("2 + 3 = %fn", client.plus(2, 3));
    printf("6 - 3 = %fn", client.minus(6, 3));
    printf("8 * 3 = %fn", client.multiplies(8, 3));
    printf("16 / 5 = %fn", client.divides(16, 5));

    transport->close();
  } catch (TException& tx) {
    printf("ERROR: %sn", tx.what());
  }
  return 0;
}

Start the server first, then run the client. If the calculation results are displayed, everything is working. Nice work.