很多年前,我有个师兄,姓唐名朝,用 Delphi 开了一套单机软件,叫“智能中医诊断系统”,就是用户输入自己的症状,他那个软件就分析这些症状,有针对性的再问用户几个问题,最后给出一两个方子。当年他开发出来后,放到高交会上去展示,效果很好,他大喜,立马定价 888 元一套,结果买了大半年都只买了几套出去,房租又贵,只好关了公司重新打工去了。
前些天刚好本地有个单位叫我给他们的 DLL 算法程序弄一下压力测试,做的过程中突然想到了我那不太走运的师兄,如果他那套程序搬上网会怎么样呢?很有技术含量的啊,算法都用了一大堆,在网上一定会大发特发的吧?光是买春药广告都不得了。。。
去年,微软开源了代码为 Casablanca 的 C++ REST SDK,目的主要是为了让 C++ 编程时更加方便的消费 RESTful 服务。但最近它新增了一项功能:New experimental features such as HTTP Listener library
,正是这项功能的出现,我认为它会成为一个比较具有潜力的项目,简单的说,利用 Casablanca,可以搭建起一个原生代码与云计算服务之间的双向桥梁,轻轻松松的把那些用 C、C++、Delphi 甚至是 VB 写的单机程序转变成 Web 服务,让单机时代的优秀创意在云计算时代重新闪亮!
作为示例,我们开发一个 DLL,这个 DLL 调用 OpenCV 的图形处理功能将一张彩色图片转换为灰度图,代码如下,很简单:
#include "stdafx.h"
#include "TestFuncDLL.h"
#include <stdexcept>
#include <opencv\cv.h>
#include <opencv\highgui.h>
using namespace cv;
using namespace std;
namespace TestFuncs{
int MyTestFuncs::count = 0;
int MyTestFuncs::Gray(char* input, char* output){
Mat image;
image = imread(input, 1);
if (!image.data){
printf(" No image data \n ");
return -1;
}
Mat gray_image;
cvtColor(image, gray_image, CV_BGR2GRAY);
imwrite(output, gray_image);
return 0;
}
}
具体步骤为:
#define _CRT_SECURE_NO_DEPRECATE
,不然链接 OpenCV 时会报错。这里值得一提的是NuGet,这玩意儿用起来比 gem 还方便,搜索程序包,然后点安装,就完事了。如果不看看 Ruby 之外的东西,真不知道各家的技术都这么好用了。
不废话,直接上代码。不到 100 行就可以把一个原生代码函数开放成一个 json web service,您还等什么呢,压箱子底的老旧代码都拿出来吧!
这是侦听程序:
#include "stdafx.h"
#include "TestListener.h"
#include "TestFuncDLL.h"
#include <iostream>
#include <string>
using namespace std;
namespace TestService{
TestListener::TestListener(const http::uri& url) : m_litenser(http_listener(url))
{
m_litenser.support(methods::GET, tr1::bind(&TestListener::handle_get, this, tr1::placeholders::_1));
m_litenser.support(methods::POST, tr1::bind(&TestListener::handle_post, this, tr1::placeholders::_1));
try{
m_litenser.open().then([&](){ wcout << "Test listener started." << endl; }).wait();
while (true);
}
catch (exception const & e){
wcout << e.what() << endl;
}
}
void TestListener::handle_get(http_request request){
utility::string_t greeting = U("您好!您现在访问的是测试引擎...");
json::value::field_map answer;
answer.push_back(make_pair(json::value::string(U("greeting")), json::value::string(greeting)));
request.reply(status_codes::OK, json::value::object(answer));
}
int wCharToChar(const wchar_t *orig, char *nstring){
size_t origsize = wcslen(orig) + 1;
size_t convertedChars = 0;
wcstombs_s(&convertedChars, nstring, origsize, orig, _TRUNCATE);
return 0;
}
void handle_request(http_request request,
function<void(json::value &, json::value::field_map &)> action)
{
json::value::field_map answer;
request
.extract_json()
.then([&answer, &action](pplx::task<json::value> task) {
try
{
auto & jvalue = task.get();
if (!jvalue.is_null())
{
action(jvalue, answer);
}
}
catch (http_exception const & e)
{
wcout << e.what() << endl;
}
}).wait();
request.reply(status_codes::OK, json::value::object(answer));
}
void TestListener::handle_post(http_request request){
handle_request(
request,
[](json::value & jvalue, json::value::field_map & answer)
{
char input[1000];
char output[1000];
for (auto const & e : jvalue)
{
if (e.first.is_string() && e.second.is_string())
{
auto key = e.first.as_string();
auto value = e.second.as_string();
if (key == U("input")){
wCharToChar(value.c_str(), input);
}
if (key == U("output")){
wCharToChar(value.c_str(), output);
}
}
}
if (strlen(input) > 0 && strlen(output) > 0){
int result = TestFuncs::MyTestFuncs::Gray(input, output);
}
});
}
}
这是主程序:
// TestWebService.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "MainListerner.h"
#include "TestListener.h"
#include <thread>
using namespace std;
void mainListener(){
TestService::MainListener listener(L"http://localhost/welcome");
}
void testListener(){
TestService::TestListener listener(L"http://localhost/test");
}
int _tmain(int argc, _TCHAR* argv[])
{
cout << "启动测试引擎服务..." << endl;
thread t_main(mainListener);
thread t_test(testListener);
t_main.join();
t_test.join();
}
在主项目中,除了要添加 DLL 引用外,同样,也用 NuGet 添加 c++ rest sdk 支持。
TestFuncDLL.dll 完成的功能很简单,就是调用 OpenCV 将一张彩色图片转换为灰色图片,计算量肯定比你们的程序小很多。
TestWebService.exe 是封装好了的程序,它提供两个接口:http://localhost/welcome 和 http://localhost/testhttp://localhost/welcome,其中 只接受 GET 请求,/test 接口接受 GET 和 POST 请求。
GET 请求均只是简单的返回一个欢迎字符串,比如 welcome 返回 {"greeting":"您好!中科院重庆分院美妆引擎正在为您服务..."}。
/test 接口接受 json 数据的 POST 请求,json 数据格式为:{"input": "源图片路径", "output": "输出图片路径"},显然,这里只考虑了本机访问的情况,如果外网访问,这两个参数应是“地址”而不是“路径”。
由于 Casablanca 目前还只是实验性的,目前只支持 Windows 7, 8 和 Linux,其中 Linux 下不支持 https、认证等功能。
当然也用得上,比如在计算机视觉、图形图像等领域,C++ 被大量使用,这些代码除了以传统的中间件(如 ICE 等)的形式整合到线上外,Casablanca 的出现,我们可以有了另一种比较轻便、廉价的选择。
最后,我有一个疑问,相比 Casablanca 把原生代码封装成 WebService,还有一种更加简单的方案,就是直接用 Ruby/Python/.NET 等调用 DLL。是啊,直接调用 DLL 就完了嘛,需要 卡萨布兰卡 吗?需要吗,不需要吗?我怎么本能感觉用语言直接调用 DLL 不太靠谱呢?
各位分析一下,直接调用是不是真的不靠谱,哪点不靠谱了,还是仅仅只是我自己想复杂了?