Thrift C++ 服務器和客戶端開發實例--學習筆記

C++服務器和客戶端開發
參考:thrift下C++服務器和客戶端開發
參考:ThriftUsageC++
參考:Thrift 入門教程
Thrift環境搭建:
Ubuntu環境下Thrift的安裝、編譯以及測試
ubuntu下thrift的安裝

RPC框架之Thrift分析

Thrift示例二:
Thrift C++ 服務器和客戶端開發實例–學習筆記二
ostringstream的清空方法

1 實現這個例子,我們大致要做以下幾部分事情:

(1)書寫.thrift文件

(2)生成cpp文件

(3)編寫客戶端

(4)編譯cpp文件並執行

下面是詳細的步驟:

(1)書寫.thrift文件

學生信息是有結構的,所以我們使用thrift的struct即可,爲了達到通信的目的,我們必須使用service。

所以最後書寫成的student.thrift文件內容如下:

struct Student{
    1: i32 sno,
    2: string sname,
    3: bool ssex,
    4: i16 sage,
}

service Serv{
     void put(1: Student s),
     i32 icall(1: Student s),
     string scall(1: Student s),
     /*
         string& srcall(1: Student s),
         -----------------------------
         -thrift -r --gen cpp student.thrift
         -error:
         -   [ERROR:/root/test/thrift/student.thrift:12] (last token was '&')
         -   syntax error
         -   [FAILURE:/root/test/thrift/student.thrift:12] Parser error during include pass.
         -----------------------------
     */
     Student stcall(1: Student s),
}

(2)生成cpp文件

生成cpp文件很簡單,只需要一個thrift命令即可:

/home/xiaoshe/opt/bin/thrift -r –gen cpp student.thrift

–gen 後指定生成的語言,生成的cpp存儲在目錄gen-cpp下

命令執行後,將會在./gen-cpp/目錄下生成如下文件:

Serv.cpp

Serv.h

Serv_server.skeleton.cpp

student_constants.cpp

student_constants.h

student_types.cpp

student_types.h

注意文件的大小寫:

Serv開頭的文件是由service生成的,這個關鍵字很重要,下面還會見到以它開頭的類。

student是根據student.thrift文件的名生成的。

這些文件可以進行編譯,生成最初的服務端。

(3)編寫客戶端

使用thrift命令後,我們並沒有得到我們想要的客戶端client源代碼,因此客戶端程序要由我們自己編寫實現。Client代碼如下:

#include "Serv.h"
#include <transport/TSocket.h>
#include <transport/TBufferTransports.h>
#include <protocol/TBinaryProtocol.h>

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

using boost::shared_ptr;

int main(int argc, char **argv)
{
    boost::shared_ptr<TSocket> socket(new TSocket("localhost", 9090));
    boost::shared_ptr<TTransport> transport(new TBufferedTransport(socket));
    boost::shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport));

    transport->open();

    //調用server服務
    Student s;
    s.sno = 123;
    s.sname = "hao973";
    s.ssex = 1;
    s.sage = 30;

    ServClient client(protocol);
    printf("sno=%d sname=%s ssex=%d sage=%d\n", s.sno, s.sname.c_str(), s.ssex, s.sage);
    //put
    client.put(s);
    //icall scall
    std::string strname = "";
    client.scall(strname, s);
    printf("icall=%d, scall=%s\n", client.icall(s), strname.c_str());
    //stcall
    client.stcall(stu, s);
    printf("student sno=%d sname=%s ssex=%d sage=%d\n", stu.sno, stu.sname.c_str(), stu.ssex, stu.sage);

    transport->close();

    return 0;
}

同時修改服務端的代碼及文件Serv_server.skeleton.cpp中:
在Serv_server.skeleton.cpp文件中put函數中添加:
//add by self
printf(“sno=%d sname=%s ssex=%d sage=%d\n”, s.sno, s.sname.c_str(), s.ssex, s.sage);

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

#include "Serv.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 boost::shared_ptr;

class ServHandler : virtual public ServIf {
 public:
  ServHandler() {
    // Your initialization goes here
  }

  void put(const Student& s) {
    // Your implementation goes here
    printf("put\n");
    printf("sno=%d sname=%s ssex=%d sage=%d\n", s.sno, s.sname.c_str(), s.ssex, s.sage);
  }

  int32_t icall(const Student& s) {
    // Your implementation goes here
    printf("icall\n");
    printf("sno=%d sname=%s ssex=%d sage=%d\n", s.sno, s.sname.c_str(), s.ssex, s.sage);
    return s.sage;
  }

  void scall(std::string& _return, const Student& s) {
    // Your implementation goes here
    printf("scall\n");
    printf("sno=%d sname=%s ssex=%d sage=%d\n", s.sno, s.sname.c_str(), s.ssex, s.sage);
    _return = s.sname;
  }

  void stcall(Student& stu, const Student& s) {
    // Your implementation goes here
    printf("stcall\n");
    printf("sno=%d sname=%s ssex=%d sage=%d\n", s.sno, s.sname.c_str(), s.ssex, s.sage);
    stu.sno     = s.sno + 1;
    stu.sname   = s.sname + "123";
    stu.ssex    = s.ssex;
    stu.sage    = s.sage + 10;
  }

};

int main(int argc, char **argv) {
  int port = 9090;
  shared_ptr<ServHandler> handler(new ServHandler());
  shared_ptr<TProcessor> processor(new ServProcessor(handler));
  shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));
  shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());
  shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());

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

2 編譯鏈接:

編譯程序命令:
//服務器
g++ -g -Wall -I./ -I/usr/local/include/thrift Serv.cpp student_types.cpp student_constants.cpp Serv_server.skeleton.cpp -L/usr/local/lib/*.so -lthrift -o server
//客戶端
g++ -g -Wall -I./ -I/usr/local/include/thrift Serv.cpp student_types.cpp student_constants.cpp Client.cpp -L/usr/local/lib/*.so -lthrift -o client

注意:
在自己的環境下要是把-L/usr/local/lib/.so -lthrift
放在 .cpp 和 .o文件前面會出現鏈接錯誤。部分錯誤如下:
/tmp/ccvtijvB.o:在函數‘ServClient::recv_put()’中:
/root/test/thrift/gen-cpp/Serv.cpp:197:對‘apache::thrift::TApplicationException::read(apache::thrift::protocol::TProtocol*)’未定義的引用
/tmp/ccvtijvB.o:在函數‘ServProcessor::dispatchCall(apache::thrift::protocol::TProtocol*, apache::thrift::protocol::TProtocol*, std::__cxx11::basic_string

CC=g++ -g -Wall

CFLAGS = -I. -I/usr/local/include/thrift 

LFLAGS = -L/usr/local/lib

LDEXEFLAGS = -lthrift 

OBJS = Serv.o \
        student_types.o \
        student_constants.o 

all:client server


Serv.o: Serv.cpp
    $(CC) $(CFLAGS) -c $^ -o $@
student_types.o: student_types.cpp
    $(CC) $(CFLAGS) -c $^ -o $@
student_constants.o: student_constants.cpp
    $(CC) $(CFLAGS) -c $^ -o $@
Serv_server.skeleton.o: Serv_server.skeleton.cpp
    $(CC) $(CFLAGS) -c $^ -o $@
Client.o: Client.cpp
    $(CC) $(CFLAGS) -c $^ -o $@

server: $(OBJS) Serv_server.skeleton.o
    $(CC) $(LFLAGS) $(OBJS) Serv_server.skeleton.o  $(LDEXEFLAGS) -o $@

client: $(OBJS)  Client.o
    $(CC) $(LFLAGS) $(OBJS)  Client.o  $(LDEXEFLAGS) -o $@


clean:
    rm -f ./*.o client server

3 運行結果:
先啓動server.
再運行client.
server輸出:

# ./server 
put
sno=123 sname=hao973 ssex=1 sage=30
scall
sno=123 sname=hao973 ssex=1 sage=30
icall
sno=123 sname=hao973 ssex=1 sage=30
stcall
sno=123 sname=hao973 ssex=1 sage=30

client執行結果:

# ./client 
sno=123 sname=hao973 ssex=1 sage=30
icall=30, scall=hao973
student sno=124 sname=hao973123 ssex=1 sage=40
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章