首页 > 代码库 > thrift在windows的编译/安装--c++版

thrift在windows的编译/安装--c++版

前言:
  thrift是出于Facebook的rpc网络编程框架, 其对跨平台和多语言的支持优于google protobuf, 但thrift在java/c#语言上应用比较多, 资料也丰富, 在windows平台的c++这块, 资料相对较少, 而且编译也麻烦. 这篇博客主要记录对thrift在windows上的编译和使用过程, 不涉及原理, 也不具体涉及应用.如有不足, 请各位指正.

执行过程
1. 下载并安装Visual Studio
notice: visual studio 有windows版本限制, 比如visual studio 2013在windows 7就安装不了
参考网址: http://www.visualstudio.com/zh-cn/visual-studio-2013-compatibility-vs
系统: windows7 + visual studio 2012

2. boost安装/编译/链接
  具体步骤如下:
  *) 下载boost
    1. 下载 boost_1_55_0.zip
  *) 编译boost
    1. 执行 bootstrap.bat
    2. 执行 b2.exe (编译的时间较长, 请耐心等待)
  *) 验证boost
    在virtual studio的window console工程属性中添加如下:
    1. 附加包含目录: $BOOST_HOME
    2. 附加库目录: $BOOST_HOME\stage\lib
    3. 编写如下代码进行编译/运行认证

 1 #include <iostream>
 2 #include <string>
 3 #include <boost/regex.hpp>
 4 int main()
 5 {
 6  boost::regex pattern("\\w+@\\w+(\\.\\w+)*");
 7  std::string mail("xxx@gmail.com");
 8  
 9  if ( boost::regex_match(mail, pattern) ) {
10   std::cout << mail << " is a valid mail address!" << std::endl;
11  } else {
12   std::cout << mail << " is not a valid mail address!" << std::endl;
13  }
14 }

  安装boost和配置visual studio的参考网址如下所示:
  http://blog.csdn.net/stanfordzhang/article/details/8587282
  http://www.cnblogs.com/me115/archive/2010/10/08/1845825.html
  http://www.cnblogs.com/chuncn/archive/2012/09/10/2679026.html

3. libevent的编译/安装/链接
  *) 参考的编译/安装过程网页
  http://blog.s135.com/libevent_windows/
  *) 下载libevent
  http://libevent.org/
  *) 编译libevent
  遇到的编译错误处理方案
  http://10305101ivy.blog.163.com/blog/static/584765892012227322607/

  http://blog.csdn.net/boyxiaolong/article/details/17057063
  evutil.c添加如下行:

 1 #ifdef WIN32
 2 #include <winsock2.h>
 3 #include <ws2tcpip.h>
 4 #pragma comment(lib,"ws2_32.lib") 
 5 #define WIN32_LEAN_AND_MEAN
 6 #include <windows.h>
 7 #undef WIN32_LEAN_AND_MEAN
 8 #include <io.h>
 9 #include <tchar.h>
10 #endif

  nmake /f Makefile.nmake
  生成libevent_core.lib libevent_extras.lib libevent.lib
  若遇到头文件找不到的问题, 需要手动修改Makefile.nmake文件, 添加相关的头文件路径

CFLAGS=/IWIN32-Code /Iinclude /Icompat /DWIN32 /DHAVE_CONFIG_H /I. /I"C:\Program Files (x86)\Windows Kits\8.0\Include\um" /I"C:\Program Files (x86)\Windows Kits\8.0\Include\shared" /I"C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include"
    具体添加的原则是编译缺那个头文件, 就去添加相关的系统头文件目录   
    编写libevent代码编译/运行成功
1 #include <event.h>
2 #include <stdio.h>
3 
4 int main()
5 {
6     const char *version = event_get_version();
7     printf("%s\n",version);
8     return 0;
9 }
    附加依赖项: ws2_32.lib , libevent_core.lib ,libevent.lib, libevent_extras.lib

4. thrift的编译/链接
  *)下载thrift 0.9.0源码
  下载网址: http://archive.apache.org/dist/thrift
  *)thrift依赖的库
  http://www.coder4.com/archives/3777
  thrift 依赖 boost库(1.4.7), thriftnb 依赖 boost/libevent库
  http://www.iteye.com/problems/87958
  thrift在编译过程中, 会遇到二义性
  “_wassert”: 对重载函数的调用不明确
  void _wassert(const wchar_t *,const wchar_t *,unsigned int)
  void apache::thrift::protocol::_wassert(const wchar_t *,const wchar_t *,unsigned int)
  解决方案:
  这算命令空间污染的问题, 添加::, 使得对_wassert的调用采用全局声明的那个函数

1 assert.h
2  
3 #define assert(_Expression) (void)( (!!(_Expression)) || (_wassert(_CRT_WIDE(#_Expression), _CRT_WIDE(__FILE__), __LINE__), 0) )
4  
5 #define assert(_Expression) (void)( (!!(_Expression)) || (::_wassert(_CRT_WIDE(#_Expression), _CRT_WIDE(__FILE__), __LINE__), 0) )
6      
    测试验证:
    编写 hello.thrift 文件
1 namespace cpp test
2  
3 service HelloService {
4     string hello(1: string username); 
5 }
  thrift.exe -gen cpp hello.thrift 
1 2014/05/07 11:35 9,215 HelloService.cpp
2 2014/05/07 11:26 7,117 HelloService.h
3 2014/05/07 11:26 1,422 HelloService_server.skeleton.cpp
4 2014/05/07 11:35 268 hello_constants.cpp
5 2014/05/07 11:35 357 hello_constants.h
6 2014/05/07 11:35 202 hello_types.cpp
7 2014/05/07 11:35 367 hello_types.h 
    编译执行, 遇到10093错误, 如何去解决?
    WSANOTINITIALISED,  which means WSAStartup() has not been called yet.
    编译完成后运行时会报WSAStartup错误    
    解决方案:
    http://hi.baidu.com/fsx92/item/9f7a96efd33f9f1b585dd88c                  
    编写测试case
  服务端代码:
 1 class HelloServiceHandler : virtual public HelloServiceIf {
 2  public:
 3   HelloServiceHandler() {
 4     // Your initialization goes here
 5   }
 6  
 7   void hello(std::string& _return, const std::string& username) {
 8  _return = "hello " + username;
 9   }
10  
11 };
12  
13 int main(int argc, char **argv) {
14   int port = 9090;
15  
16   TWinsockSingleton::create();      // 需要用户自己添加, 进行WSAStartup的初始化, 算是windows 版的thrift的一个疏忽
17  
18   shared_ptr<HelloServiceHandler> handler(new HelloServiceHandler());
19   shared_ptr<TProcessor> processor(new HelloServiceProcessor(handler));
20   shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));
21   shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());
22   shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());
23  
24   TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);
25   server.serve();
26   return 0;
27 }
    客户端代码 
 1  
 2 #include "HelloService.h"
 3 #include <thrift/protocol/TBinaryProtocol.h>
 4 #include <thrift/server/TSimpleServer.h>
 5 #include <thrift/transport/TBufferTransports.h>
 6 #include <thrift/transport/TSocket.h>
 7  
 8 using namespace ::apache::thrift;
 9 using namespace ::apache::thrift::protocol;
10 using namespace ::apache::thrift::transport;
11 using namespace ::apache::thrift::server;
12  
13 using boost::shared_ptr;
14  
15 using namespace ::test;
16  
17 int main()
18 {
19  
20  shared_ptr<TTransport> socket(new TSocket("127.0.0.1", 9090));
21  shared_ptr<TTransport> transport(new TBufferedTransport(socket));
22  shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport));
23  
24  HelloServiceClient client(protocol);
25  
26  try {
27   transport->open();
28   std::string res;
29   client.hello(res, "lilei");
30   std::cout << res << std::endl;
31  } catch(TException &e) {
32   std::cout << e.what() << std::endl;
33  }
34  
35  return 0;
36 }
37  
    推荐做法:
    对依赖库的整理, 这是一个好的习惯
    每个库单独创建一个头文件目录, 和库文件目录, 所有的库统一在同一个库仓库下,  c++的库管理不如java的maven那么方便, 又进入一个石器时代, 库的维护需要开发者手动去支持, 但这是种很好的工程实践.     
    
    repository目录为顶级的仓库目录, 以boost为例, boost表示库名, 之下boost/1.55.0为boost的具体的某一版本, 而boost/0.55.0/include为这个版本的头文件目录, boost/0.55.0/lib为这个版本的lib库目录