Thrift之c++实例

一、c++实例

1.下载与安装thrift工具

http://thrift.apache.org/download/

http://archive.apache.org/dist/thrift/

2.编写Thrift文件(定义接口,结构,异常等),保存为test.thrift

struct User{

  1:i64 id,

  2:string name,

  3:i32 age,

  4:bool vip

}

service Test{

 i32 add(1:i32 a,2:i32 b)

 User getById(1:i64 id)

}

3.生成接口代码

把thrift-0.9.3.exe和test.thrift文件放在同一个目录。

进入DOS命令执行:thrift-0.9.3.exe -r --gen cpp test.thrift

--gen后指定生成的语言,生成的cpp存储在目录gen-cpp下。

生成文件gen-cpp/Test.h、Test.cpp等,如下图。

Thrift之c++实例_第1张图片

Test_server.skeleton.cpp,就是C++服务端的main函数入口文件,里面使用了TSimpleServer作为TCP服务,性能较低,但是实现简单,比较合适做进程管理类的接口。

 

4、服务端代码

1)、新建vc工程。

2)、将上面的文件拷贝到工程目录下,Test_server.skeleton.cpp,就是C++服务端的main函数入口文件。

 

// This autogenerated skeleton file illustrates how to build a server.
// You should copy it to another filename to avoid overwriting it.

#include "Test.h"
#include 
#include 
#include 
#include 

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

using boost::shared_ptr;

class TestHandler : virtual public TestIf {
 public:
  TestHandler() {
    // Your initialization goes here
	  Init();
  }

  int32_t add(const int32_t a, const int32_t b) {
    // Your implementation goes here
    printf("add\n");
	return a+b;
  }

  void getById(User& _return, const int64_t id) {
    // Your implementation goes here
	User *user;
	std::list::iterator iter = m_listUser.begin(),iterEnd = m_listUser.end();
	for(iter; iter!=iterEnd; iter++)
	{
		user = *iter;
		if(user->id == id)
		{
			_return.id = user->id;
			_return.__set_name(user->name.c_str());
			_return.age = user->age;
			_return.__set_vip(user->vip);
			break;
		}
	}
	printf("getById\n");
  }

protected:
	void Init(){
		m_listUser.clear();
		int n = 0;
		for(n=0; n<10; n++){
			User * user = new User();
			user->id = n+1;
			user->age = 18+n;
			sprintf((char *)user->name.data(),"name_%d",n+1);
			//插入
			m_listUser.push_back(user);
		}


	}
	std::list m_listUser;
};

int main(int argc, char **argv) {
  int port = 9090;
  shared_ptr handler(new TestHandler());
  shared_ptr processor(new TestProcessor(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;
}

 

//需要引入thrift对于cpp的lib头文件所在目录,openssl头文件所在目录,boost头文件所在目录。

//需要引用lib文件,LibThrift.lib libeay32MT.libssleay32MT.lib

 thrift对于cpp的lib文件所在目录,比如:

e:\libthrift\libthrift-0.9.3\lib\cpp\src\thrift\windows

e:\libthrift\libthrift-0.9.3\lib\cpp\src

//如果用到thrift的异步通讯方式,需要libevent库支持,lib库文件libevent.lib libevent_core.lib libevent_extras.lib。
用VS2008的nmake编译(libevent)C语言代码的方法

 

1)用vcvars32.bat设置环境变量或直接通过Visual Studio 2008 命令提示进入vc环境
D:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin>vcvars32.bat
D:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin>"D:\Program Files (x86
)\Microsoft Visual Studio 9.0\Common7\Tools\vsvars32.bat"
Setting environment for using Microsoft Visual Studio 2008 x86 tools.
2)切换到源代码所在的目录
D:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin>cd E:\libevent-2.0.21-stable-vs2008
对源代码需要做些修改。
添加宏定义
在libevent-2.0.21-stable文件夹下找到下面三个文件:
event_iocp.c
evthread_win32.c
listener.c
打开并在开头加上宏定义:
#define _WIN32_WINNT 0x0500

因为event_iocp.c里用到头文件里的函数定义,如InitializeCriticalSectionAndSpinCount,
会包含,而这个头文件里这个函数是这样定义的:
#if (_WIN32_WINNT >= 0x0403)
WINBASEAPI
BOOL WINAPI
InitializeCriticalSectionAndSpinCount(
__out LPCRITICAL_SECTION lpCriticalSection,
__in DWORD dwSpinCount
);

WINBASEAPI
DWORD
WINAPI
SetCriticalSectionSpinCount(
__inout LPCRITICAL_SECTION lpCriticalSection,
__in DWORD dwSpinCount
);
#endif

所以要定义_WIN32_WINNT这个宏,而且值要大于0x0403。
如果没有这个宏或不满足条件,编译器会假定这个函数没有定义,
等到链接时再寻找它,这样这个函数的符号就假定返回一个int,
而显示标准库文件里这个函数不是返回int,所以在链接时就会找不到这个函数符号。
注意:宏一定要定义在#include 之前,不然还是没有作用。
3)用nmake.exe编译
E:\libevent-2.0.21-stable-vs2008>nmake /f Makefile.nmake

libevent在windows下使用步骤详解
参考http://www.mamicode.com/info-detail-504940.html

5、客户端代码

 

1)、新建vc工程。

2)、将上面的文件(除Test_server.skeleton.cpp外)拷贝到工程目录下。

3)、编写客户端代码。

// Client.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include 
#include 
#include 
#include 
#include "Test.h"

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

using boost::shared_ptr;
using namespace std;

int main(int argc, char* argv[])
{
	boost::shared_ptr socket(new TSocket("localhost", 9090));
	//对接nonblockingServer时必须的,对普通server端时用boost::shared_ptr transport(new TBufferedTransport(socket));
	//boost::shared_ptr transport(new TFramedTransport(socket));
	boost::shared_ptr transport(new TBufferedTransport(socket));

	boost::shared_ptr protocol(new TBinaryProtocol(transport));
	TestClient client(protocol);

	try
	{
		//设置发送、接收、连接超时
		socket->setConnTimeout(5000);
		socket->setRecvTimeout(5000);
		socket->setSendTimeout(5000);

		transport->open();

		//insert your code here  
		int nResult = client.add(100,200);
		printf("add(%d,%d) = %d\n",100,200,nResult);
		User findUser;
		findUser.id = -100;
		client.getById(findUser,1);
		printf("%s\n",findUser.name.c_str());

		transport->close();

		printf("send success!\n");
	}
	catch (...)
	{
	}

	return 0;
}

 

//需要引入thrift对于cpp的lib头文件所在目录,openssl头文件所在目录。

 

//需要引用lib文件,LibThrift.lib libeay32MT.libssleay32MT.lib

 

二、VS2008上编译thrift的库文件

1、下载thrift-0.9.3.tar.gz源码,源码针对cpp的编译自带的是vs2010版本。

2、需要第三方库boost、libevent、openssl支持。

libevent-2.0.21-stable.tar.gzhttps://github.com/downloads/libevent/libevent/libevent-2.0.21-stable.tar.gz

boost 1.54.0

http://www.boost.org/users/history/version_1_54_0.html

编译好的boost 1.54.0下载地址:https://sourceforge.net/projects/boost/files/boost-binaries/1.54.0

OpenSSL 1.0.0g

VC2008下使用OpenSSL 1.0.0g(免编译)

http://blog.csdn.net/akof1314/article/details/7241829

3、新建vc工程,编译。

用Project From Existing Code...创建工程。
将 cpp/src/thrift加入工程,修改项目配置类型修改成静态库。
在Solution Explorer中,
去除 qt目录。
添加 boost包含目录、openssl所包含目录。
添加 src为包含目录,不然 #include 出错。
VC2008缺少 stdint.h,创建到thrift/windows目录下,并添加为include目录。

Thrift之c++实例_第2张图片

下图为程序目录结构

Thrift之c++实例_第3张图片
#pragma once

#include

typedef boost::int8_t int8_t;

typedef boost::uint8_t uint8_t;

typedef boost::int16_t int16_t;

typedef boost::uint16_t uint16_t;

typedef boost::int32_t int32_t;

typedef boost::uint32_t uint32_t;

typedef boost::int64_t int64_t;

typedef boost::uint64_t uint64_t;

 

#define INT8_MIN    ((int8_t)_I8_MIN)

#define INT8_MAX    _I8_MAX

#define INT16_MIN   ((int16_t)_I16_MIN)

#define INT16_MAX   _I16_MAX

#define INT32_MIN   ((int32_t)_I32_MIN)

#define INT32_MAX   _I32_MAX

#define INT64_MIN   ((int64_t)_I64_MIN)

#define INT64_MAX   _I64_MAX

#define UINT8_MAX   _UI8_MAX

#define UINT16_MAX  _UI16_MAX

#define UINT32_MAX  _UI32_MAX

#define UINT64_MAX  _UI64_MAX

去除VC2010 thrift.sln中没有的文件,无法编译:
Mutex.cpp, PosixThreadFactory.cpp, Monitor.cpp
TEvhttpClientChannel.cpp TEvhttpServer.cpp
TServer.cpp TNonblockingServer.cpp
TSSLSocket.cpp TSSLServerSocket.cpp
TZlibTransport.cpp

加上宏 HAVE_CONFIG_H,或者 force include force_inc.h.
windows/tr1/functional中
#include
改为
#include

三、vc2015编译thrift-0.10.0

1、thrift-0.10.0包含libthrift和libthriftnb两个工程
libthrift:阻塞thrift rpc
libthriftnb:非阻塞thrift rpc,需要使用libevent库。
编译时,上面只需要openssl库的头文件路径。但在引用thrift库文件时,需要加载openssl库的头文件路径和lib文件路径。

2、编译过程:
1)、将openssl1.0.1c库文件夹拷贝到与thrift同级目录下。
2)、将libevent-2.0.21-stable库文件夹拷贝到与thrift同级目录下。libevent的版本需要与libthrift中帮助文档中指定的版本一致。
3)、修改thrift--lib--cpp中3rdparty.props文件中,修改boost、libevent、openssl宏。
4)、打开libthrift.sln,编译。
注意:在编译静态库时,需要添加TServerFramework、TConnectedClient文件到工程中,否则不会导出这两个类,在引用thrift文件时,会报找不到函数的链接错误。

3rdparty.props文件



  
  
    F:\develop\boost_1_66_0
    F:\develop\openssl1.0.1c
    F:\develop\libevent-2.0.21-stable
  
  
  
  
    
      $(BOOST_ROOT)
      true
    
    
      $(OPENSSL_ROOT_DIR)
      true
    
    
      $(LIBEVENT_ROOT)
      true
    
  

    F:\develop\boost_1_66_0
    F:\develop\openssl1.0.1c
    F:\develop\libevent-2.0.21-stable

三个开源库定义宏里面的内容值,根据所在电脑的路径进行修改。修改完成之后重新打开解决方案。

代码下载

四、libthrift阻塞模式中的服务器,线程中处理服务器代码,以便有空闲时间处理其他业务。

#include "stdafx.h"

// This autogenerated skeleton file illustrates how to build a server.
// You should copy it to another filename to avoid overwriting it.

#include "Test.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 

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

class TestHandler : virtual public TestIf {
public:
	TestHandler() {
		// Your initialization goes here
		Init();
	}

	int32_t add(const int32_t a, const int32_t b) {
		// Your implementation goes here
		printf("add\n");
		return a + b;
	}


	void getById(User& _return, const int64_t id) {
		// Your implementation goes here
		User *user;
		std::list::iterator iter = m_listUser.begin(), iterEnd = m_listUser.end();
		for (iter; iter != iterEnd; iter++)
		{
			user = *iter;
			if (user->id == id)
			{
				_return.id = user->id;
				_return.__set_name(user->name.c_str());
				_return.age = user->age;
				_return.__set_vip(user->vip);
				break;
			}
		}
		printf("getById\n");
	}

protected:
	void Init() {
		m_listUser.clear();
		int n = 0;
		for (n = 0; n < 10; n++) {
			User * user = new User();
			user->id = n + 1;
			user->age = 18 + n;
			sprintf((char *)user->name.data(), "name_%d", n + 1);
			//插入
			m_listUser.push_back(user);
		}
	}

std::list m_listUser;
};


class TStartObserver : public apache::thrift::server::TServerEventHandler {
public:
	TStartObserver() : awake_(false) {}
	virtual void preServe() {
		apache::thrift::concurrency::Synchronized s(m_);
		awake_ = true;
		m_.notifyAll();
	}
	void waitForService() {
		apache::thrift::concurrency::Synchronized s(m_);
		while (!awake_)
			m_.waitForever();
	}

private:
	apache::thrift::concurrency::Monitor m_;
	bool awake_;
};

int main(int argc, char **argv) 
{
	int nRet = 0;
        //start
	int port = 9090;
	boost::shared_ptr server;
	boost::shared_ptr observer;
	boost::shared_ptr serverThread;
	boost::shared_ptr threadManager = NULL;
	boost::shared_ptr serverTransport;
	try
	{
		boost::shared_ptr handler(new TestHandler());
		boost::shared_ptr processor(new TestProcessor(handler));
		serverTransport.reset(new TServerSocket(port));
		boost::shared_ptr transportFactory(new TBufferedTransportFactory());
		boost::shared_ptr protocolFactory(new TBinaryProtocolFactory());
		threadManager = ThreadManager::newSimpleThreadManager(10);
		boost::shared_ptr threadFactory = boost::shared_ptr(new PlatformThreadFactory());
		threadManager->threadFactory(threadFactory);
		threadManager->start();
		server.reset(new TThreadPoolServer(processor, serverTransport, transportFactory, protocolFactory, threadManager));
		observer.reset(new TStartObserver());
		server->setServerEventHandler(observer);
		serverThread.reset();
		serverThread = threadFactory->newThread(server);
		serverThread->start();
		observer->waitForService();
		printf("done.\n");
	}
	catch (const std::exception& ex)
	{
		printf(ex.what());
		nRet = -3;
	}
	catch (...)
	{
		nRet = -1;
	}

        //模拟其他业务处理
	while (true)
	{
		Sleep(200);
	}
        ////////////////////////////////////////////////////////////////////

	//stop
	if (server != NULL)
	{
		server->stop();
	}
	if (serverTransport != NULL)
	{
		serverTransport->close();
	}
	if (threadManager != NULL)
	{
		threadManager->stop();
	}
	if (serverThread != NULL)
	{
		serverThread->join();
	}

	return 0;
}

 

你可能感兴趣的:(RPC)