简介
什么是 GoogleProtocol Buffer?
来自网上的文字介绍:
Google ProtocolBuffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准。他们用于 RPC 系统和持续数据存储系统。
Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。目前提供了 C++、Java、Python 三种语言的 API。
介绍一个简单的rpc例子
协议如下toolrpc.proto:
package tool;
option cc_generic_services = true;
message Error
{
optional int32 code=1;
optional string message =2;
}
message echoReq
{
optional string info = 3;
}
message echoRsp
{
message MData
{
optional string info = 3;
}
optional Error error=3;
optional MData result =6;
}
service toolrpcsrv
{
rpc echo(echoReq) returns (echoRsp);
}
Protobuf 工具使用上面的toolrpc.proto文件生成一个toolrpcsrv作为服务端继承实现的类,和一个toolrpcsrv_Stub使用于客户端调用。
命令如下:
protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR path/to/file.proto
参考命令:
protoc --cpp_out=./ toolrpc.proto
服务pb定义
主要包括服务定义和接口定义。
service toolrpcsrv
{
rpc echo(echoReq) returns (echoRsp);
}
生成的服务类
继承Service 类,并生成接口需函数接口。
class toolrpcsrv : public ::google::protobuf::Service {
protected:
// This class should be treated as an abstract interface.
inline toolrpcsrv() {};
public:
virtual ~toolrpcsrv();
virtual void echo(::google::protobuf::RpcController* controller,
const ::tool::echoReq* request,
::tool::echoRsp* response,
::google::protobuf::Closure* done)
{
// ... read request and fill in response ...
done->Run();
}
};
生成的客户端调用类
1、继承服务端类
class toolrpcsrv_Stub : public toolrpcsrv {
public:
toolrpcsrv_Stub(::google::protobuf::RpcChannel* channel);
toolrpcsrv_Stub(::google::protobuf::RpcChannel* channel,
::google::protobuf::Service::ChannelOwnership ownership);
~toolrpcsrv_Stub();
//接口调用函数
void echo(::google::protobuf::RpcController* controller,
const ::tool::echoReq* request,
::tool::echoRsp* response,
::google::protobuf::Closure* done);
private:
//调用网络发送接口
::google::protobuf::RpcChannel* channel_;
};
RpcController 说明
class LIBPROTOBUF_EXPORT RpcController {
public:
inline RpcController() {}
virtual ~RpcController();
public:
//1、渠道调用失败后需要设置错误信息返回到业务测
virtual void SetFailed(const string& reason) = 0;
};
RpcChannel说明
class LIBPROTOBUF_EXPORT RpcChannel {
public:
inline RpcChannel() {}
virtual ~RpcChannel();
//1、由toolrpcsrv_Stub的客户端接口调用,通过网络协议和服务端交互。
virtual void CallMethod(const MethodDescriptor* method,
RpcController* controller,
const Message* request,
Message* response,
Closure* done) = 0;
};
服务端实现
1、 接收客户端请求,并解析获取request,以及解析server_id和method_id(此处把server_name和method_name作为id标识)。
2、 获取到MethodDescriptor, 通过Service->CallMethod函数调用到对应的服务端接口函数。
3、 处理完业务逻辑后由response带出响应消息,然后回包。
//1、实现接收网络接口
int iRet = recv(cli_fd, recv_buf, contentlen);
//2、解析服务id,函数id(使用函数名和服务名进行标识)
string service_name, method_name;
if(!ParseMethodFullName(sUrl, service_name, method_name))
{
return -1;
}
tool::echoReq * _pRequest = new tool::echoReq();
tool::echoRsp * _pResponse = new tool::echoRsp();
//3、解析请求包
_pRequest->ParseFromString(_body);
//4、查找rpc服务指针
google::protobuf::Service* _pService = ServicePool::Instance()->FindService(service_name);
if(_pService == NULL)
{
return -1;
}
MyRpcController * _pController = new MyRpcController();
//5、获取服务描述符
ServiceDescriptor * service_desc = const_cast<ServiceDescriptor * >( _pService->GetDescriptor() );
//6、获取函数描述符
MethodDescriptor * method = const_cast<MethodDescriptor * >( service_desc->FindMethodByName(method_name) );
//7、通过基类callmethod调用接口处理
_pService->CallMethod( method, _pController, _pRequest, _pResponse, NULL);
string sBody;
//8、序列化响应包
_pResponse->SerializeToString(&sBody);
//9、网络回包
iRet = send( cli_fd, sBuf);
if(iRet < 0)
{
return -1;
}
客户端调用流程说明
1、 MyRpcChannel, MyRpcController, service客户端调用资源准备。
2、 请求包准备,调用rpc接口函数,toolrpcsrv_Stub的接口函数调用MyRpcChannel的CallMethod函数,通过CallMethod发送和接收服务器的响应包。
3、 处理response消息。
//1、new 客户端调用网络渠道
channel = new MyRpcChannel("127.0.0.1", 3332);
controller = new MyRpcController;
//2、new 客户端
service = new tool::toolrpcsrv_Stub(channel);
// 3、Set up the request.
request.set_info("protocol buffers test call protobuf rpc");
//4、 Execute the RPC.
service->echo(controller, &request, &response, google::protobuf::NewCallback(&Done));
总结
到此为止rpc简单搭建已经完成,自己有想当然的成分,如果有不足的地方欢迎批评指正。
完整测试代码如下(通过设置iSysSeverityLevel控制打印):
服务端
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/time.h>
#include <fcntl.h>
#include <sys/file.h>
#include <unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include <stdarg.h>
#include <sstream>
#include <google/protobuf/service.h>
#include <google/protobuf/descriptor.h>
#include "toolrpc.pb.h"
using namespace std;
enum log_level {FATAL,ERROR,WARN,INFO,DEBUG,MAX_LEVAL};
void Printf(const char * format, ...)
{
char Data[4096];
va_list v;
va_start(v,format);
vsnprintf(Data, sizeof(Data), format,v);
va_end(v);
/*if (endline)*/
printf("%s\r\n", Data);
}
int iSysSeverityLevel= 0;
//系统日志
#undef LOGSYS
#ifndef LOGSYS
#define LOGSYS(level, fmt, args...) do {\
if (level <= iSysSeverityLevel)\
{\
Printf("(%d)%s(%d):" fmt " ", level, __FUNCTION__,__LINE__,##args);\
}\
} while (0)
#endif
int HandleHttpInputTotalPkg( void *sBuf, int iLen, int & contentlen)
{
char *p;
char * pHeadEnd;
if (!(pHeadEnd=strstr((char *)sBuf, "\r\n\r\n")))
{
//protect self
if (iLen > 8*1024)
{
return -1;
}
return 0; //not finished
}
if((p=strcasestr((char *)sBuf,"Content-Length:"))==NULL)
return -2;
p += 15;
while(*p==' '||*p=='\t'||*p=='\r')
p++;
contentlen = atoll(p);
int iTotalLen=(pHeadEnd-(char *)sBuf)+4+contentlen;
if(iLen<iTotalLen)
return 0; //no recv finished
return iTotalLen;
}
int create_srv()
{
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if(listen_fd < 0)
{
LOGSYS(ERROR, "listen_fd : [%d], error : [d] ", listen_fd);
return -1;
}
struct sockaddr_in my_addr;
memset(&my_addr,0,sizeof(my_addr));
my_addr.sin_family = AF_INET;
//my_addr.sin_port = htonl(3332);
my_addr.sin_port = ntohs(3332);
//my_addr.sin_addr.s_addr = inet_addr("0.0.0.0");
//my_addr.sin_addr.s_addr = INADDR_ANY;
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
printf("accept port %d\r\n",my_addr.sin_port );
//int addr_len = sizeof( struct sockaddr);
int iRet = bind(listen_fd, (struct sockaddr *)&my_addr, sizeof( struct sockaddr) ) ;
if(iRet < 0)
{
LOGSYS(ERROR, "bind : [%d], error : [d] ", listen_fd);
return -2;
}
iRet = listen(listen_fd, 5);
if(iRet < 0)
{
LOGSYS(ERROR, "listen : [%d], error : [d] ", listen_fd);
return -2;
}
return listen_fd;
}
int recv(int cli_fd, string & recv_buf,int & contentlen)
{
char buf[1024 * 1024 ] = {0};
while(true)
{
int iRLen = recv(cli_fd, buf, 1024 * 1024, 0);
if(iRLen == 0)
{
//RUN_LOG(ERROR,"sock_read close , ip : port %s:%d", ip,port);
LOGSYS(ERROR, "recv iRLen : [%d], error : [d] ", iRLen);
return -1;
//break;
}
else if(iRLen < 0)
{
LOGSYS(ERROR, "recv iRLen : [%d], error : [d] ", iRLen);
return -1;
}
recv_buf.append(buf, iRLen);
int iTotal_len = HandleHttpInputTotalPkg((void *)recv_buf.c_str(), recv_buf.length(), contentlen);
if(iTotal_len > 0)
{
LOGSYS(INFO,"end recv buf %d", recv_buf.length());
LOGSYS(DEBUG,"recv buf %s", recv_buf.c_str());
//break;
return 0;
}
LOGSYS(DEBUG,"recv buf %d", recv_buf.length());
LOGSYS(DEBUG,"recv buf %s", recv_buf.c_str());
}
return -1;
}
int send(int cli_fd, const string & send_buf)
{
uint32_t send_offset = 0;
while(true)
{
int iLen = send(cli_fd, send_buf.c_str()+send_offset, send_buf.length(), 0);
if(iLen == 0)
{
LOGSYS(DEBUG,"send buf %d", iLen);
return -1;
}
else if(iLen < 0)
{
LOGSYS(DEBUG,"send buf %d", iLen);
return -1;
}
send_offset += iLen;
if(send_offset == send_buf.length())
{
//发送完
break;
}
}
return 0;
}
using namespace google::protobuf;
class MyRpcController : public google::protobuf::RpcController {
public:
MyRpcController() {}
virtual ~MyRpcController(){};
// Client-side methods ---------------------------------------------
// These calls may be made from the client side only. Their results
// are undefined on the server side (may crash).
// Resets the RpcController to its initial state so that it may be reused in
// a new call. Must not be called while an RPC is in progress.
//virtual void Reset() = 0;
virtual void Reset() {};
// After a call has finished, returns true if the call failed. The possible
// reasons for failure depend on the RPC implementation. Failed() must not
// be called before a call has finished. If Failed() returns true, the
// contents of the response message are undefined.
//virtual bool Failed() const = 0;
virtual bool Failed() const { return true;};
// If Failed() is true, returns a human-readable description of the error.
//virtual string ErrorText() const = 0;
virtual string ErrorText() const {return ""; };
// Advises the RPC system that the caller desires that the RPC call be
// canceled. The RPC system may cancel it immediately, may wait awhile and
// then cancel it, or may not even cancel the call at all. If the call is
// canceled, the "done" callback will still be called and the RpcController
// will indicate that the call failed at that time.
//virtual void StartCancel() = 0;
virtual void StartCancel() {};
// Server-side methods ---------------------------------------------
// These calls may be made from the server side only. Their results
// are undefined on the client side (may crash).
// Causes Failed() to return true on the client side. "reason" will be
// incorporated into the message returned by ErrorText(). If you find
// you need to return machine-readable information about failures, you
// should incorporate it into your response protocol buffer and should
// NOT call SetFailed().
//virtual void SetFailed(const string& reason) = 0;
virtual void SetFailed(const string& reason) {return ; };
// If true, indicates that the client canceled the RPC, so the server may
// as well give up on replying to it. The server should still call the
// final "done" callback.
//virtual bool IsCanceled() const = 0;
virtual bool IsCanceled() const {return false; };
// Asks that the given callback be called when the RPC is canceled. The
// callback will always be called exactly once. If the RPC completes without
// being canceled, the callback will be called after completion. If the RPC
// has already been canceled when NotifyOnCancel() is called, the callback
// will be called immediately.
//
// NotifyOnCancel() must be called no more than once per request.
//virtual void NotifyOnCancel(Closure* callback) = 0;
virtual void NotifyOnCancel(Closure* callback) {};
void SetFailed(int code, const string& errMsg)
{
this->m_sErr_msg = errMsg;
this->m_iErr_code = code;
};
private:
int m_iErr_code;
string m_sErr_msg;
};
class MyRpcChannel: public google::protobuf::RpcChannel {
public:
MyRpcChannel(string ip, int port)
{
this->m_ip = ip;
this->m_port = port;
}
public:
~MyRpcChannel(){};
// Call the given method of the remote service. The signature of this
// procedure looks the same as Service::CallMethod(), but the requirements
// are less strict in one important way: the request and response objects
// need not be of any specific class as long as their descriptors are
// method->input_type() and method->output_type().
void CallMethod(const google::protobuf::MethodDescriptor* method,
google::protobuf::RpcController* controller,
const google::protobuf::Message* request,
google::protobuf::Message* response,
google::protobuf::Closure* done);
int pack_request(const string & method_id, const google::protobuf::Message* request, string & sOut);
int uppack_response(char * pRecv_buf, int iRecv_len, google::protobuf::Message* response);
private:
string m_ip;
int m_port;
};
int MyRpcChannel::pack_request(const string & method_id, const google::protobuf::Message* request, string & sOut)
{
int iBodySize=request->ByteSize();
string sBodySize = "";
std::stringstream tmpStream;
tmpStream << iBodySize;
tmpStream >>sBodySize;
sOut = "POST /"+method_id;
sOut += " HTTP/1.1\r\n";
sOut += "Content-type: application/octet-stream\r\n";
sOut += "Host: 127.0.0.1\r\n";
sOut += "Content-Length: "+sBodySize+"\r\n\r\n";
sOut.resize(sOut.size()+iBodySize);
request->SerializeToArray((void *)(const_cast<char*>(sOut.data())+(sOut.size()-iBodySize)),iBodySize);
return 0;
}
int MyRpcChannel::uppack_response(char * pRecv_buf, int iRecv_len, google::protobuf::Message* response)
{
char *pCur;
if (!(pCur=strstr((char *)pRecv_buf, "\r\n\r\n")))
{
return -1;
}
pCur+=4;
string err;
response->Clear();
int iRet=0;
int iBodySize=iRecv_len-(pCur-pRecv_buf);
if(!response->ParseFromArray(pCur,iBodySize))
{
return -2;
}
return iRet;
}
class ServicePool
{
public:
ServicePool()
{
}
virtual ~ServicePool()
{
}
static ServicePool* Instance (void)
{
static ServicePool g_service_pool;
return &g_service_pool;
}
bool RegisterService(google::protobuf::Service* service, bool take_ownership = true)
{
google::protobuf::ServiceDescriptor* svc_desc = const_cast<google::protobuf::ServiceDescriptor* >(service->GetDescriptor() );
const string svc_name = svc_desc->full_name();
LOGSYS(DEBUG,"RegisterService , svc_name[%s]", svc_name.c_str());
m_svc_name2svc.insert(make_pair(svc_name, service) );
return true;
}
google::protobuf::Service* FindService(const std::string& svc_name)
{
auto iter = m_svc_name2svc.find(svc_name);
if(iter == m_svc_name2svc.end())
{
return NULL;
}
return m_svc_name2svc[svc_name];
}
private:
map<string, google::protobuf::Service* > m_svc_name2svc;
};
class EchoToolServiceImpl : public tool::toolrpcsrv {
public:
EchoToolServiceImpl() {};
void CallMethod(const ::google::protobuf::MethodDescriptor* method,
::google::protobuf::RpcController* controller,
const ::google::protobuf::Message* request,
::google::protobuf::Message* response,
::google::protobuf::Closure* done)
{
LOGSYS(INFO,"EchoToolServiceImpl CallMethod" );
toolrpcsrv::CallMethod( method, controller, request, response, done);
}
virtual void echo(::google::protobuf::RpcController* controller,
const ::tool::echoReq* request,
::tool::echoRsp* response,
::google::protobuf::Closure* done)
{
response->mutable_result()->set_info(request->info());
response->mutable_error()->set_code(0);
response->mutable_error()->set_message("SUCCESS");
LOGSYS(INFO,"EchoToolServiceImpl echo" );
return ;
}
};
std::string & trim(std::string &s)
{
if(s.empty())
{
return s;
}
s.erase(0, s.find_first_not_of(" "));
s.erase(s.find_last_not_of(" ")+1);
return s;
}
bool ParseMethodFullName(
const std::string& method_full_name,
std::string & service_name,
std::string & method_name)
{
std::string::size_type pos = method_full_name.rfind('.');
if (pos == std::string::npos)
{
return false;
}
service_name = method_full_name.substr(0, pos);
method_name = method_full_name.substr(pos + 1);
return true;
}
int do_task(int cli_fd)
{
while(true)
{
string recv_buf;
int contentlen = 0;
int iRet = recv(cli_fd, recv_buf, contentlen);
if(iRet < 0)
{
LOGSYS(DEBUG,"recv failed %s", recv_buf.c_str());
return -1;
}
string sHttp_head = recv_buf.substr(recv_buf.find("POST")+4, recv_buf.find("HTTP") - recv_buf.find("POST")-4 );
LOGSYS(DEBUG,"sHttp_head [%s]", sHttp_head.c_str() );
string sUrl = trim(sHttp_head);
LOGSYS(DEBUG,"sUrl [%s]", sUrl.c_str() );
sUrl = sUrl.substr(1, sUrl.length()-1 );
LOGSYS(DEBUG,"sUrl [%s]", sUrl.c_str() );
string service_name, method_name;
if(!ParseMethodFullName(sUrl, service_name, method_name))
{
LOGSYS(DEBUG,"ParseMethodFullName failed, sUrl %s", sUrl.c_str() );
return -1;
}
string _body = recv_buf.substr(recv_buf.find("\r\n\r\n")+4, contentlen);
tool::echoReq * _pRequest = new tool::echoReq();
tool::echoRsp * _pResponse = new tool::echoRsp();
_pRequest->ParseFromString(_body);
google::protobuf::Service* _pService = ServicePool::Instance()->FindService(service_name);
if(_pService == NULL)
{
LOGSYS(DEBUG,"FindService failed, service_name[%s], method_name[%s]", service_name.c_str(), method_name.c_str() );
return -1;
}
MyRpcController * _pController = new MyRpcController();
ServiceDescriptor * service_desc = const_cast<ServiceDescriptor * >( _pService->GetDescriptor() );
MethodDescriptor * method = const_cast<MethodDescriptor * >( service_desc->FindMethodByName(method_name) );
_pService->CallMethod( method, _pController, _pRequest, _pResponse, NULL);
string sBody;
_pResponse->SerializeToString(&sBody);
string sCOntentType;
sCOntentType="Content-type: application/octet-stream\r\n";
int iBodySize = sBody.length();
//iBodySize=_pResponse->ByteSize();
string sBuf;
sBuf.append("HTTP/1.1 ");
sBuf=sBuf+" 200 "+"\r\n";
sBuf.append("Server: pbrpc-server\r\n");
sBuf.append(sCOntentType);
sBuf.append("Content-Length: ");
stringstream tmpstream;
tmpstream << iBodySize;
string tmpStr;
tmpstream >> tmpStr;
sBuf.append(tmpStr);
sBuf.append("\r\n\r\n");
sBuf.append(sBody);
iRet = send( cli_fd, sBuf);
if(iRet < 0)
{
LOGSYS(DEBUG,"send failed %s", sBuf.c_str());
return -1;
}
}
}
int run_srv()
{
EchoToolServiceImpl *impl = new EchoToolServiceImpl();
//rpc_server.Start()
ServicePool::Instance()->RegisterService(impl, true);
int listen_fd = -1;
listen_fd = create_srv();
if( listen_fd < 0)
{
LOGSYS(ERROR, "create_srv : [%d], error : [d] ", listen_fd);
return -1;
}
printf("listen [%d] success !\r\n", listen_fd);
while(true)
{
int addr_len = sizeof( struct sockaddr_in);
struct sockaddr_in remote_addr;
int cli_fd = accept(listen_fd, (struct sockaddr *)&remote_addr, (socklen_t*)&addr_len);
if(cli_fd < 0)
{
LOGSYS(ERROR, "accept : [%d], error : [d] ", listen_fd);
return -2;
}
printf("accept success, one con fd : [%d] !\r\n", cli_fd);
printf("accept client %s/n",inet_ntoa(remote_addr.sin_addr));
// recv
if(do_task(cli_fd) < 0)
{
close(cli_fd);
continue;
}
}
return 0;
}
int main(int argc, char *argv[])
{
int iRet = run_srv();
if(iRet < 0)
{
printf("run_srv failed !\r\n");
return -1;
}
return 0;
}
客户端
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/time.h>
#include <fcntl.h>
#include <sys/file.h>
#include <unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include <stdarg.h>
#include <sstream>
#include <google/protobuf/service.h>
#include <google/protobuf/descriptor.h>
#include "toolrpc.pb.h"
using namespace std;
enum log_level {FATAL,ERROR,WARN,INFO,DEBUG,MAX_LEVAL};
void Printf(const char * format, ...)
{
char Data[4096];
va_list v;
va_start(v,format);
vsnprintf(Data, sizeof(Data), format,v);
va_end(v);
/*if (endline)*/
printf("%s\r\n", Data);
}
int iSysSeverityLevel= 5;
//系统日志
#undef LOGSYS
#ifndef LOGSYS
#define LOGSYS(level, fmt, args...) do {\
if (level <= iSysSeverityLevel)\
{\
Printf("(%d)%s(%d):" fmt " ", level, __FUNCTION__,__LINE__,##args);\
}\
} while (0)
#endif
int HandleHttpInputTotalPkg( void *sBuf, int iLen, int & contentlen)
{
char *p;
char * pHeadEnd;
if (!(pHeadEnd=strstr((char *)sBuf, "\r\n\r\n")))
{
//protect self
if (iLen > 8*1024)
{
return -1;
}
return 0; //not finished
}
if((p=strcasestr((char *)sBuf,"Content-Length:"))==NULL)
return -2;
p += 15;
while(*p==' '||*p=='\t'||*p=='\r')
p++;
contentlen = atoll(p);
int iTotalLen=(pHeadEnd-(char *)sBuf)+4+contentlen;
if(iLen<iTotalLen)
return 0; //no recv finished
return iTotalLen;
}
int connect_srv(string ip, int port)
{
int con_fd = socket(AF_INET, SOCK_STREAM, 0);
if(con_fd < 0)
{
LOGSYS(ERROR, "listen_fd : [%d], error : [d] ", con_fd);
return -1;
}
struct sockaddr_in my_addr;
memset(&my_addr,0,sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = inet_addr(ip.c_str());
my_addr.sin_port = htons(port);
printf("accept port %d\r\n",my_addr.sin_port );
//int addr_len = sizeof( struct sockaddr);
int iRet = connect(con_fd, (struct sockaddr *)&my_addr, sizeof( struct sockaddr) ) ;
if(iRet < 0)
{
LOGSYS(ERROR, "bind : [%d], error : [d] ", con_fd);
close(con_fd);
return -2;
}
return con_fd;
}
int recv(int cli_fd, string & recv_buf,int & contentlen)
{
char buf[1024 * 1024 ] = {0};
while(true)
{
int iRLen = recv(cli_fd, buf, 1024 * 1024, 0);
if(iRLen == 0)
{
//RUN_LOG(ERROR,"sock_read close , ip : port %s:%d", ip,port);
LOGSYS(ERROR, "recv iRLen : [%d], error : [d] ", iRLen);
return -1;
//break;
}
else if(iRLen < 0)
{
LOGSYS(ERROR, "recv iRLen : [%d], error : [d] ", iRLen);
return -1;
}
recv_buf.append(buf, iRLen);
int iTotal_len = HandleHttpInputTotalPkg((void *)recv_buf.c_str(), recv_buf.length(), contentlen);
if(iTotal_len > 0)
{
LOGSYS(INFO,"end recv buf %d", recv_buf.length());
LOGSYS(DEBUG,"recv buf %s", recv_buf.c_str());
//break;
return 0;
}
LOGSYS(DEBUG,"recv buf %d", recv_buf.length());
LOGSYS(DEBUG,"recv buf %s", recv_buf.c_str());
}
return -1;
}
int send(int cli_fd, const string & send_buf)
{
uint32_t send_offset = 0;
while(true)
{
int iLen = send(cli_fd, send_buf.c_str()+send_offset, send_buf.length(), 0);
if(iLen == 0)
{
LOGSYS(DEBUG,"send buf %d", iLen);
return -1;
}
else if(iLen < 0)
{
LOGSYS(DEBUG,"send buf %d", iLen);
return -1;
}
send_offset += iLen;
if(send_offset == send_buf.length())
{
//发送完
break;
}
}
return 0;
}
int TcpsendRcv(const string ip, int port, string send_buf, string & recv_buf)
{
int con_srv = -1;
con_srv = connect_srv(ip, port);
if( con_srv < 0)
{
LOGSYS(ERROR, "create_srv : [%d], error : [d] ", con_srv);
return -1;
}
int iRet = send(con_srv, send_buf);
if(iRet < 0)
{
LOGSYS(DEBUG,"send failed buf %d", iRet);
return -2;
}
int contentlen;
iRet = recv(con_srv, recv_buf, contentlen);
if(iRet < 0)
{
LOGSYS(DEBUG,"send failed buf %d", iRet);
return -3;
}
printf("listen [%d] success !\r\n", con_srv);
return 0;
}
using namespace google::protobuf;
class MyRpcController : public google::protobuf::RpcController {
public:
MyRpcController() {}
virtual ~MyRpcController(){};
// Client-side methods ---------------------------------------------
// These calls may be made from the client side only. Their results
// are undefined on the server side (may crash).
// Resets the RpcController to its initial state so that it may be reused in
// a new call. Must not be called while an RPC is in progress.
//virtual void Reset() = 0;
virtual void Reset() {};
// After a call has finished, returns true if the call failed. The possible
// reasons for failure depend on the RPC implementation. Failed() must not
// be called before a call has finished. If Failed() returns true, the
// contents of the response message are undefined.
//virtual bool Failed() const = 0;
virtual bool Failed() const { return true;};
// If Failed() is true, returns a human-readable description of the error.
//virtual string ErrorText() const = 0;
virtual string ErrorText() const {return ""; };
// Advises the RPC system that the caller desires that the RPC call be
// canceled. The RPC system may cancel it immediately, may wait awhile and
// then cancel it, or may not even cancel the call at all. If the call is
// canceled, the "done" callback will still be called and the RpcController
// will indicate that the call failed at that time.
//virtual void StartCancel() = 0;
virtual void StartCancel() {};
// Server-side methods ---------------------------------------------
// These calls may be made from the server side only. Their results
// are undefined on the client side (may crash).
// Causes Failed() to return true on the client side. "reason" will be
// incorporated into the message returned by ErrorText(). If you find
// you need to return machine-readable information about failures, you
// should incorporate it into your response protocol buffer and should
// NOT call SetFailed().
//virtual void SetFailed(const string& reason) = 0;
virtual void SetFailed(const string& reason) {return ; };
// If true, indicates that the client canceled the RPC, so the server may
// as well give up on replying to it. The server should still call the
// final "done" callback.
//virtual bool IsCanceled() const = 0;
virtual bool IsCanceled() const {return false; };
// Asks that the given callback be called when the RPC is canceled. The
// callback will always be called exactly once. If the RPC completes without
// being canceled, the callback will be called after completion. If the RPC
// has already been canceled when NotifyOnCancel() is called, the callback
// will be called immediately.
//
// NotifyOnCancel() must be called no more than once per request.
//virtual void NotifyOnCancel(Closure* callback) = 0;
virtual void NotifyOnCancel(Closure* callback) {};
void SetFailed(int code, const string& errMsg)
{
this->m_sErr_msg = errMsg;
this->m_iErr_code = code;
};
private:
int m_iErr_code;
string m_sErr_msg;
};
class MyRpcChannel: public google::protobuf::RpcChannel {
public:
MyRpcChannel(string ip, int port)
{
this->m_ip = ip;
this->m_port = port;
}
public:
~MyRpcChannel(){};
// Call the given method of the remote service. The signature of this
// procedure looks the same as Service::CallMethod(), but the requirements
// are less strict in one important way: the request and response objects
// need not be of any specific class as long as their descriptors are
// method->input_type() and method->output_type().
void CallMethod(const google::protobuf::MethodDescriptor* method,
google::protobuf::RpcController* controller,
const google::protobuf::Message* request,
google::protobuf::Message* response,
google::protobuf::Closure* done);
int pack_request(const string & method_id, const google::protobuf::Message* request, string & sOut);
int uppack_response(char * pRecv_buf, int iRecv_len, google::protobuf::Message* response);
private:
string m_ip;
int m_port;
};
int MyRpcChannel::pack_request(const string & method_id, const google::protobuf::Message* request, string & sOut)
{
int iBodySize=request->ByteSize();
string sBodySize = "";
stringstream tmpStream;
tmpStream << iBodySize;
tmpStream >>sBodySize;
sOut = "POST /"+method_id;
sOut += " HTTP/1.1\r\n";
sOut += "Content-type: application/octet-stream\r\n";
sOut += "Host: 127.0.0.1\r\n";
sOut += "Content-Length: "+sBodySize+"\r\n\r\n";
sOut.resize(sOut.size()+iBodySize);
request->SerializeToArray((void *)(const_cast<char*>(sOut.data())+(sOut.size()-iBodySize)),iBodySize);
return 0;
}
int MyRpcChannel::uppack_response(char * pRecv_buf, int iRecv_len, google::protobuf::Message* response)
{
char *pCur;
if (!(pCur=strstr((char *)pRecv_buf, "\r\n\r\n")))
{
return -1;
}
pCur+=4;
string err;
response->Clear();
int iRet=0;
int iBodySize=iRecv_len-(pCur-pRecv_buf);
if(!response->ParseFromArray(pCur,iBodySize))
{
return -2;
}
return iRet;
}
void MyRpcChannel::CallMethod(const google::protobuf::MethodDescriptor* method,
google::protobuf::RpcController* controller,
const google::protobuf::Message* request,
google::protobuf::Message* response,
google::protobuf::Closure* done)
{
// prepare controller
MyRpcController* cntl = dynamic_cast<MyRpcController*>(controller);
if(cntl==NULL)
{
printf("controller is NULL?\n");
return;
}
//cntl->FillFromMethodDescriptor(method);
//pack msg
string sOut;
//if(PackRequest(cntl->SequenceId(), cntl->MethodId(),request,sOut)<0)
string method_id = method->full_name();
if(pack_request( method_id,request,sOut)<0)
{
printf("pack request failed\n");
cntl->SetFailed(503, "packe request failed\n");
return;
}
string sRecv;
//int iRet= MtTcpsendRcv(&_stAddr,(void *)(const_cast<char*>(sOut.data())), sOut.size(), sRecv,iRecv, cntl->Timeout(), hconn::HandleHttpInputNotTotalPkg);
int iRet = TcpsendRcv(m_ip, m_port, sOut, sRecv);
if(iRet<0)
{
cntl->SetFailed(501, "TcpsendRcv failed");
}
else
{
//unpack msg
if((iRet=uppack_response(const_cast<char*>(sRecv.data()),sRecv.length(),response))<0)
{
//printf("unpack error:%d\n",iRet);
cntl->SetFailed(502, "uppack_response failed\n");
return;
}
}
printf("CallMethod done\n");
return;
}
google::protobuf::RpcChannel* channel;
google::protobuf::RpcController* controller;
tool::toolrpcsrv* service;
tool::echoReq request;
tool::echoRsp response;
void Done() {
delete service;
delete channel;
delete controller;
}
void do_task() {
// You provide classes MyRpcChannel and MyRpcController, which implement
// the abstract interfaces protobuf::RpcChannel and protobuf::RpcController.
channel = new MyRpcChannel("127.0.0.1", 3332);
controller = new MyRpcController;
// The protocol compiler generates the SearchService class based on the
// definition given above.
service = new tool::toolrpcsrv_Stub(channel);
// Set up the request.
request.set_info("protocol buffers test call protobuf rpc");
// Execute the RPC.
service->echo(controller, &request, &response, google::protobuf::NewCallback(&Done));
LOGSYS(DEBUG,"recv_buf buf %s", response.DebugString().c_str());
return;
}
int run_cli()
{
do_task();
return 0;
}
int main(int argc, char *argv[])
{
int iRet = run_cli();
if(iRet < 0)
{
printf("run_cli failed !\r\n");
return -1;
}
return 0;
}