Introduction
The Apache Thrift is a software framework used to facilitate binary communication. Based on RPC (remote procedure call) it was developed by Facebook and provides the complete stack for creation clients/server solutions. Cross-language is one of the most useful feature of Thrift by providing a lot of supported programming languages (C#,C++, Java, Php, Node.js, Cocoa, …). The common struct and service should be written in .thrift files using the interface definition language of the framework.
Here we want to create a simple client/server solution first in C++ then between C++ and C#.
- I. Downloads
- II. Setup prerequisites
- III. Compile thrift sources in Cpp
- IV. Generation of the “POCService”
- V. Create our client/server solution
I. Downloads
Example: Visual Studio Solution Sources
Download
Example: Visual Studio Solution Sources + Executables
Download
Thrift compiler (used to generate class files from a *.thrift definition file):
http://www.apache.org/dyn/closer.cgi?path=/thrift/0.9.2/thrift-0.9.2.exe
Thrift sources (Extract thrift-0.9.2.tar.gz)
http://wwwftp.ciril.fr/pub/apache/thrift/0.9.2/thrift-0.9.2.tar.gz
Boost
http://cznic.dl.sourceforge.net/project/boost/boost-binaries/1.57.0/boost_1_57_0-msvc-12.0-64.exe
OpenSSL (compiled libraries for Visual Studio)
http://www.npcglib.org/~stathis/blog/precompiled-openssl/
II. Setup prerequisites
Boost installation
OpenSSL
You can download OpenSSL sources then compile it or download directly the compiled library on the following url: http://www.npcglib.org/~stathis/blog/precompiled-openssl/
Select the version which targeted the MSVC 2013 compiler.
Extract “openssl-1.0.1l-vs2013.7z” into “C:\libraries\openssl-1.0.1l-vs2013”
LibEvent
Download LibEvent sources at the following url: http://softlayer-ams.dl.sourceforge.net/project/levent/libevent/libevent-2.0/libevent-2.0.22-stable.tar.gz
Extract “libevent-2.0.22-stable.tar.gz” into ”C:\libraries\libevent-2.0.22-stable”
Compile the library:
Open a visual command prompt “C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\Shortcuts\VS2013 x64 Cross Tools Command Prompt”
Then navigate to the sources folder:
>cd C:\libraries\libevent-2.0.22-stable |
Compile using the nmake file:
> nmake -f Makefile.nmake |
“C:\libraries\libevent-2.0.22-stable” contains 3 static libraries:
- libevent.lib
- libevent_extras.lib
- libevent_core.lib
III. Compile thrift sources in Cpp
Now, all prerequisites libraries are stored in the “C:\libraries” folder.
Download thrift sources (thrift-0.9.2.tar.gz) on http://wwwftp.ciril.fr/pub/apache/thrift/0.9.2/thrift-0.9.2.tar.gz then extract into (C:\libraries \thrift-0.9.2)
Open the solution located at “C:\libraries \thrift-0.9.2\lib\cpp\thrift.sln”
I’m currently using the Visual Studio 2015 CTP6, therefore I get the following error when compiling the 2 projects (libthrift, libthriftnb).
For both of them, go to project properties then change the option “General > Platform Toolset” from “Visual Studio 2015 (v140)” to “Visual Studio 2013 (v120)”
Compile again, we get new errors (Cannot open include file: ‘boost/static_assert.hpp’, …)
We have to resolve all dependencies (Boost, OpenSSL, LibEvent)
Compile libthrift
Define “Additional include directories”
Open the project property pages:
Then set “C/C++ > General > Additional Include Directories” set the following values:
- C:\libraries\libevent-2.0.22-stable\include
- C:\libraries\openssl-1.0.1l-vs2013\include
- C:\libraries\boost_1_57_0
- C:\libraries\boost_1_57_0\boost
Define “Additional library directories”
Open the project property pages:
Then set “Librarian > General > Additional Library Directories” set the following values:
- C:\libraries\openssl-1.0.1l-vs2013\lib64
- C:\libraries\boost_1_57_0\lib64-msvc-12.0
- C:\libraries\libevent-2.0.22-stable
Rebuild the solution check that libthrift compile well.
Compile libthriftnb
Define “Additional include directories”
Open the project property pages then set “C/C++ > General > Additional Include Directories” set the following values:
- C:\libraries\libevent-2.0.22-stable\include
- C:\libraries\libevent-2.0.22-stable\WIN32-Code
- C:\libraries\libevent-2.0.22-stable
- C:\libraries\boost_1_57_0
Define “Additional library directories”
Open the project property pages then set “Librarian > General > Additional Library Directories” set the following values:
- C:\libraries\libevent-2.0.22-stable
- C:\libraries\boost_1_57_0\lib64-msvc-12.0
Rebuild the solution check that libthriftnb compile well.
IV. Generation of the “POCService”
Define our service definition
First of all we have to write a .thrift file that contains objects and service definitions.
Read the following document for detailed explanations http://thrift.apache.org/docs/idl
In our example we want to create a simple service to add and get objects named “ObjectPosition”. The following file contains our struct definition and the POCService methods.
pocservice.thrift:
/** * Define our sample service * */ struct ObjectPosition { 1: double X = 0, 2: double Y = 0, 3: string Identifier, } service POCService { void ping(), void AddObjectPosition(1:ObjectPosition position), list<ObjectPosition> GetAllObjectPositions() } |
Generate C++ and C# classes
Open a command prompt then change directory to the same folder as pocservice.thrift. Execute the following commands to generate C++ and C# classes:
>thrift-0.9.2.exe --gen cpp pocservice.thrift >thrift-0.9.2.exe --gen csharp pocservice.thrift |
Two folders were created: “gen-cpp” and “gen-csharp”.
V. Create our client/server solution
We will create a solution which included 3 projects:
- ThriftPOC.Server
- ThriftPOC.Client
- ThriftPOC.Shared
Server will listen to calls
Client will perform calls
Shared will contains common objects definition between server and client projects.
Creation
Create a new “Win32 project”
Select “Static library” and uncheck “precompiled header” and “Security Development Lifecycle (SDL) checks”.
Our shared project is created, just add the generated files from the “gen-cpp” folder (except “POCService_server.skeleton.cpp”)
Configuration
Go to project property pages to set “Platform Toolset” to “Visual Studio 2013 (v120)”
The generated classes reference boost and the thrift framework. We have to define these prerequisites in project option.
Define “Additional include directories”
Open the project property pages then set “C/C++ > General > Additional Include Directories” set the following values:
- C:\libraries\boost_1_57_0
- C:\libraries\thrift-0.9.2\lib\cpp\src
Define “Additional library directories”
Open the project property pages then set “Librarian > General > Additional Library Directories” set the following values:
- C:\libraries\boost_1_57_0\lib64-msvc-12.0
- C:\libraries\thrift-0.9.2\lib\cpp\Debug
C++ Server project
Creation
Now we will create the server project. Select Win32 project.
Select a “Console application” then check “Empty project” (we will used the generated service skeleton generated by thrift). Uncheck “Security Development Lifecycle (SDL) checks”.
Configuration
1. The project is created, we need to add a reference to the shared project:
2. Platform Toolset
In project property pages select Platform Toolset to “Visual Studio 2013 (v120)”.
3. Additional Include Directories
C/C++ > Additional Include Directories set the values:
- C:\libraries\boost_1_57_0
- C:\libraries\thrift-0.9.2\lib\cpp\src\thrift\windows
- C:\libraries\thrift-0.9.2\lib\cpp\src
4. Additional Library Directories
Linker > General > Additional Library Directories:
- C:\libraries\boost_1_57_0\lib64-msvc-12.0
- C:\libraries\thrift-0.9.2\lib\cpp\Debug
Linker > Input > Additional Dependencies:
- libboost_thread-vc120-mt-gd-1_57.lib
- libboost_chrono-vc120-mt-gd-1_57.lib
- libthrift.lib
Code
Thrift generated for us a server skeleton named “POCService_server.skeleton.cpp”. It contains a fake implementation of our service methods.
First of all, copy the file “POCService_server.skeleton.cpp” to the project. We need to make some changes.
Includes
We need to change
#include "POCService.h" |
to
#include "../POCService.Shared/pocservice_types.h" #include "../POCService.Shared/POCService.h" |
To display some information on the console we add the iostream library:
#include <iostream> ... using namespace std; |
Override POCServiceHandler implementation
We override the generated class to implement the service definition:
class POCServiceHandler : virtual public POCServiceIf { public: POCServiceHandler() { } void ping() { printf("ping\n"); } void AddObjectPosition(const ObjectPosition& position) { dataInMemory.push_back(position); printf("AddObjectPosition\n"); } void GetAllObjectPositions(std::vector & _return) { printf("GetAllObjectPositions\n"); _return = dataInMemory; } private: vector dataInMemory; }; |
Update the main function
With the addition of std library, we have to specify the namespace of shared_ptr. Moreover we display a message that indicates server starting and ending.
int main(int argc, char **argv) { int port = 9090; boost::shared_ptr handler(new POCServiceHandler()); boost::shared_ptr processor(new POCServiceProcessor(handler)); boost::shared_ptr serverTransport(new TServerSocket(port)); boost::shared_ptr transportFactory(new TBufferedTransportFactory()); boost::shared_ptr protocolFactory(new TBinaryProtocolFactory()); TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory); cout << "Starting the server..." << endl; server.serve(); cout << "Done." << endl; return 0; } |
Let’s compile and launch the server project.
Everythings looks good, our server project is ready.
C++ Client project
Creation
Now we will create the client project. Select Win32 project.
Select a “Console application” then check “Empty project” (we will used the generated service skeleton generated by thrift). Uncheck “Security Development Lifecycle (SDL) checks”.
Configuration
1. The project is created, we need to add a reference to the shared project:
In project property pages select Platform Toolset to “Visual Studio 2013 (v120)”.
3. Additional Include Directories
C/C++ > Additional Include Directories set the values:
- C:\libraries\boost_1_57_0
- C:\libraries\thrift-0.9.2\lib\cpp\src\thrift\windows
- C:\libraries\thrift-0.9.2\lib\cpp\src
4. Additional Library Directories
Linker > General > Additional Library Directories:
- C:\libraries\boost_1_57_0\lib64-msvc-12.0
- C:\libraries\thrift-0.9.2\lib\cpp\Debug
5. Additional Dependencies
Linker > Input > Additional Dependencies:
- libboost_thread-vc120-mt-gd-1_57.lib
- libboost_chrono-vc120-mt-gd-1_57.lib
- libthrift.lib
Code
Add a new file to the project named “POCClient.cpp”
We write a small client that will process calls to the server:
#include <iostream> #include <thrift/protocol/TBinaryProtocol.h> #include <thrift/transport/TSocket.h> #include <thrift/transport/TTransportUtils.h> #include "..\POCService.Shared\pocservice_types.h" #include "..\POCService.Shared\POCService.h" using namespace std; using namespace apache::thrift; using namespace apache::thrift::protocol; using namespace apache::thrift::transport; int main() { boost::shared_ptr socket(new TSocket("localhost", 9090)); boost::shared_ptr transport(new TBufferedTransport(socket)); boost::shared_ptr protocol(new TBinaryProtocol(transport)); POCServiceClient client(protocol); try { transport->open(); ObjectPosition obj; obj.X = 10; obj.Y = 20; obj.Identifier = "Olivier"; client.AddObjectPosition(obj); cout << "AddObjectPosition()" << endl; vector objects; client.GetAllObjectPositions(objects); cout << "Size of vector: " << objects.size() << endl; } catch (TException& tx) { cout << "ERROR: " << tx.what() << endl; } return 0; } |
Test
Launch the server:
Then launch our client (set up a breakpoint in the main):
C# Client project
To validate cross-languages support, we create a C# project.
Creation
Configuration
We need to add a reference to the Thrift library framework.
Open the solution located at “C:\libraries\thrift-0.9.2\lib\csharp\src\Thrift.sln” then compile it. “Thrift.dll” appears in the Debug folder:
Go back to the POCService solution then add a reference to the fresh build Thrift.dll
Code
Go to the “gen-csharp” folder then copy “ObjectPosition.cs” and “POCService.cs” to the new project folder.
Let’s build, we get an error “The namespace ‘<global namespace>’ already contains a definition for ‘POCService’”, we just have to rename the POCService to POCServiceC.
Go to Program.cs file then perform calls to the server:
Add the following using:
using Thrift.Transport; using Thrift.Protocol; |
Write the main method:
static void Main(string[] args) { //Thrift.Transport.TTransport Transport TTransport transport = new TSocket("localhost", 9090); transport.Open(); TProtocol protocol = new TBinaryProtocol(transport); POCServiceC.Client client = new POCServiceC.Client(protocol); //call Ping() client.ping(); Console.WriteLine("Ping"); //call AddObjectPosition(obj); ObjectPosition obj = new ObjectPosition(); obj.X = 20; obj.Y = 30; obj.Identifier = "Olivier"; client.AddObjectPosition(obj); Console.WriteLine("AddObjectPosition"); //call AddObjectPosition(obj); List res = client.GetAllObjectPositions(); Console.WriteLine("Number of positions: " + res.Count); Console.ReadLine(); } |
Comments