Usually webapi implementations return textual json, xml and other strings through http get/post requests. This article uses Tomcat8 as the web server and responds to binary data with the help of the protobuf framework.
Since the protobuf protocol can be cross-language, we can use java servlet to implement the server, and C/C++ to implement the client to achieve the purpose of decoupling each module. Both parties need to set the ContentType to application/x-protobuf.
1. Prepare the proto file
package qwd.kettas; // request structure message CTestReq { optional uint32 ShopId = 1; // Shop ID } message CProduct { optional uint32 Id = 1; // item ID optional bytes Name = 2; // product name optional double price = 3; // item price } // response structure message CTestResp { optional int32 Total = 1; // total number of items repeated CProduct Product = 2; // array of products }
2. Java Servlet Service
/** * Servlet service implementation, receiving post requests, responding to protocol buffer binary data */ protected void service( HttpServletRequest req, HttpServletResponse resp ) throws ServletException, IOException { /* * Note: Filling 0 with no data in in.read() will cause parseFrom() to fail, which is not feasible. byte[] postBuf = new byte[128]; InputStream in = req.getInputStream(); int len = in.read( postBuf ); */ // protobuf parsing parameters CTestReq tReq = CTestReq.parseFrom( req.getInputStream() ); // InputStream as parameter int shopid = tReq.getShopId(); // prepare data CTestResp.Builder pBuilder = CTestResp.newBuilder(); pBuilder.setTotal(2); // product one CProduct.Builder pProBuild1 = CProduct.newBuilder(); pProBuild1.setId( shopid + 1001 ); pProBuild1.setName( ByteString.copyFrom("Xiao Ming","UTF-8") ); // String needs to be converted by ByteString, it is best to define string pProBuild1.setPrice(15.8); pBuilder.addProduct( pProBuild1 ); // product two pProBuild1 = CProduct.newBuilder(); pProBuild1.setId( shopid + 1002 ); pProBuild1.setName( ByteString.copyFrom("小王","UTF-8") ); pProBuild1.setPrice(29.8); pBuilder.addProduct( pProBuild1 ); // reply CTestResp pResp = pBuilder.build(); System.out.println( "Data: " + pResp.toString() ); // Serialization byte[] buff = pResp.toByteArray(); // Response, set the HTTP header to: application/x-protobuf resp.setContentType("application/x-protobuf"); resp.getOutputStream().write( buff ); // 发送 }
3. Client Test
// data callback function size_t OnWriteData( void* buffer, size_t size, size_t nmemb, void *lpVoid ) { std::string* str = dynamic_cast<std::string*>((std::string *)lpVoid); if ( NULL == str || NULL == buffer ) { return -1; } char* pData = (char*)buffer; str->append(pData, size * nmemb); return nmemb; } // curl simulates post request 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 ); // serialize the request if ( !req.SerializeToString(&sReq) ) return; curl_slist *m_header = NULL; m_header = curl_slist_append( m_header, "Content-Type: application/x-protobuf" ); // set message header 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 data CURLcode ret = curl_easy_perform( curl ); assert (ret == CURLE_OK); curl_easy_cleanup( curl ); curl_slist_free_all( m_header ); // deserialize data if ( !resp.ParseFromString(sResp) ) return; printf( "Response: %s\n", resp.DebugString().c_str() ); }
As a result of the execution, curl initiates a post request and returns 46 bytes of content: