通常webapi实现通过http get/post请求,返回文本型的json,xml等字符串。本文以Tomcat8为web服务器,借助protobuf框架,响应二进制数据。
由于protobuf协议能跨语言,我们可以用java servlet实现服务端,C/C++实现客户端,达到各模块解耦目的。双方需设置ContentType为application/x-protobuf。
1. 准备proto文件
package qwd.kettas; // 请求结构 message CTestReq { optional uint32 ShopId = 1; // 店铺ID }message CProduct { optional uint32 Id = 1; // 商品ID optional bytes Name = 2; // 商品名称 optional double price = 3; // 商品价格 }// 响应结构 message CTestResp { optional int32 Total = 1; // 商品总数 repeated CProduct Product = 2; // 商品数组 }
2. Java Servlet服务
/**
* Servlet 服务实现,接收post请求,响应protocol buffer二进制数据
*/
protected void service( HttpServletRequest req, HttpServletResponse resp )
throws ServletException, IOException {
/*
* 注:用in.read() 无数据部分填充0,会导致parseFrom()失败,不可行。
byte[] postBuf = new byte[128];
InputStream in = req.getInputStream();
int len = in.read( postBuf );
*/
// protobuf 解析参数
CTestReq tReq = CTestReq.parseFrom( req.getInputStream() );
// InputStream作为参数
int shopid = tReq.getShopId();
// 准备数据
CTestResp.Builder pBuilder = CTestResp.newBuilder();
pBuilder.setTotal(2);
// 产品一
CProduct.Builder pProBuild1 = CProduct.newBuilder();
pProBuild1.setId( shopid + 1001 );
pProBuild1.setName( ByteString.copyFrom("小明","UTF-8") );
// 字符串需要通过ByteString转化,最好定义string
pProBuild1.setPrice(15.8);
pBuilder.addProduct( pProBuild1 );
// 产品二
pProBuild1 = CProduct.newBuilder();
pProBuild1.setId( shopid + 1002 );
pProBuild1.setName( ByteString.copyFrom("小王","UTF-8") );
pProBuild1.setPrice(29.8);
pBuilder.addProduct( pProBuild1 );
// reponse
CTestResp pResp = pBuilder.build();
System.out.println( "Data: " + pResp.toString() );
// 序列化
byte[] buff = pResp.toByteArray();
// 响应,设置HTTP头部为: application/x-protobuf
resp.setContentType("application/x-protobuf");
resp.getOutputStream().write( buff );
// 发送
}
3. 客户端测试
// 数据回调函数
size_t OnWriteData( void* buffer, size_t size, size_t nmemb, void *lpVoid )
{
std::string* str = dynamic_cast((std::string *)lpVoid);
if ( NULL == str || NULL == buffer )
{
return -1;
}char* pData = https://www.it610.com/article/(char*)buffer;
str->append(pData, size * nmemb);
return nmemb;
}// curl模拟post请求
void TestCurl()
{
printf( "\n****************TestCurl*******************\n" );
const char *url = "http://10.14.230.7:8080/protoweb/proto";
CTestReq req;
CTestResp resp;
string sReq;
string sResp;
req.set_shopid( 1500 );
// 序列化请求
if ( !req.SerializeToString(&sReq) )
return;
curl_slist *m_header = NULL;
m_header = curl_slist_append( m_header, "Content-Type: application/x-protobuf" );
// 设置消息头CURL *curl = curl_easy_init();
curl_easy_setopt( curl, CURLOPT_HTTPHEADER, m_header );
// Header
curl_easy_setopt( curl, CURLOPT_REFERER, "nginx" );
curl_easy_setopt( curl, CURLOPT_VERBOSE, 1L );
curl_easy_setopt( curl, CURLOPT_URL, url );
curl_easy_setopt( curl, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt( curl, CURLOPT_POSTFIELDS, sReq.c_str() );
// Post
curl_easy_setopt( curl, CURLOPT_POSTFIELDSIZE, sReq.length() );
curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, OnWriteData );
curl_easy_setopt( curl, CURLOPT_WRITEDATA, (void*)&sResp );
// Accept dataCURLcode ret = curl_easy_perform( curl );
assert( ret == CURLE_OK );
curl_easy_cleanup( curl );
curl_slist_free_all( m_header );
// 反序列化数据
if ( !resp.ParseFromString(sResp) )
return;
printf( "Response: %s\n", resp.DebugString().c_str() );
}
执行结果,curl发起post请求,返回46字节内容:
文章图片
【C/C++|基于http的protobuf服务实现】
推荐阅读
- c/c++|有感 Visual Studio 2015 RTM 简介 - 八年后回归 Dot Net,终于迎来了 Mvc 时代,盼走了 Web 窗体时代...
- C/C++|C/C++ basis 02
- Qt实战|Qt+OpenCV联合开发(二十一)--图像翻转与旋转
- Qt实战|Qt+OpenCV联合开发(十四)--图像感兴趣区域(ROI)的提取
- Qt实战|Qt+OpenCV联合开发(十三)--通道分离与合并
- opencv|Qt+OpenCV联合开发(十六)--图像几何形状绘制
- Qt实战|Qt+OpenCV联合开发(十七)--随机数与随机颜色
- SNAT的MASQUERADE地址选择与端口选择
- IPTABLES的连接跟踪与NAT分析
- IPVS分析