Protobuf rpc搭建简介


简介

什么是 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;
}


 


 


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值