C++:http消息

前言

  最近的一些開發,需要用到http服務,大致是兩種,一種是我們算法端起http服務,等到後端發送消息給算法,然後算法去解析消息,得到我們要的圖像數據;第二種是,我們算法端處理完圖像之後,需要將結果發送給後端,那麼如果是後端發送給我們的,其實可以將處理完的結果返回即可;但也有一種情況是,需要我們算法去給後端發消息的。
  所以這其中就涉及兩種情形:第一種是算法起服務,第二種是算法發送http請求。
  如果算法使用的是Python,那麼起算法服務可以使用Flask這個庫,如果是算法要發送http請求,那麼可以使用requests這個庫,也可以很方便的實現。但是如果算法是C++來寫的,那麼就可以考慮將算法封裝成庫,然後給後端調用,如果後端是python,那麼可以將C++編寫的算法封裝成so或者dll庫,然後python通過ctypes導入C++的動態庫來調用,Java則是通過JNI調C++,C#也是通過調C++的動態庫實現調C++的。但是,也可以類似Python一樣,起算法服務,來接收http請求。這個時候就需要C++來寫web服務了。這方面,其實C++也有很多web庫的,但是可能用起來就沒有python的庫那麼容易了。

一、crow

  crow是一個github上找到的開源的C++web框架,這個框架是受python下的Flask啓發的,github上的介紹是:

Crow is C++ microframework for web. (inspired by Python Flask)

可見其估計功能和用法應該是和Python的Flask應該是類似的哈。該工程的結構其實也挺簡單的,核心的代碼都寫在include中的頭文件中,因此下載下來也不需要編譯成庫,直接include頭文件即可使用,當然後期我覺得還是可以將代碼的聲明和定義分離,封裝成庫,方便調用,這樣每次編譯新的應用的時候都需要重新編譯還是比較繁瑣的。
  接下來就根據crow提供的例子來寫一下,這個框架是怎麼使用的。首先是要起服務,然後去監聽某個端口的消息。用crow實現如下:

#include <iostream>
#include <string>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include "crow.h" //crow的外部頭文件
#include "base64.h" //用來編碼數據的base64的庫
int main()
{
    crow::SimpleApp app;
    app.port(8888).multithreaded().run();

    return 0;
}

在這裏插入圖片描述

這樣我的web服務就起來了,但是也看到這樣是沒有消息處理的,只是監聽消息,而不對任何消息做處理,那麼後端給發消息的時候就會出錯。接下來就記錄下個各種消息的處理。

1.1 無參數消息

  這種常見的是應該是一些我們不需要發送什麼參數,只是需要從服務端獲取一些信息的情況,比如服務的證據狀態等。這種多數時候是一個默認的服務,常見get一個ip加端口,或者有不同的信息就給不同的字段。比如我要其一個服務,然後後端需要監控我的服務是否還健在,那麼我就可以給一個默認服務或者給一個health的服務,當後端get這個地址,如果我的服務還在,就給後端返回ok等信息。實現代碼也比較簡單,如下:

int main()
{
    crow::SimpleApp app;

    CROW_ROUTE(app, "/")([](){
        return "Hello world!";
    });

    CROW_ROUTE(app, "/health")([](){
        return "OK";
    });
    app.port(8888).multithreaded().run();

    return 0;
}

Python端的發送消息的代碼如下:

import requests
def send_health():
    url_get = "http://localhost:8888/"
    r = requests.get(url_get)
    print(r.text)
if __name__ == '__main__':
    send_health()

在這裏插入圖片描述

這樣如果C++服務端收到消息,就會返回對應的信息給Python端。

1.2 返回json格式消息

  有時候,我們需要返回的消息會比較多,那麼我們可能會指定那個字段上會有什麼消息,這樣得到返回的消息後,就會去解析消息,取到所需要的字段上的信息即可,不是需要的字段則可以不去管它。C++服務端的實現代碼如下:

CROW_ROUTE(app, "/json")
([]{
    crow::json::wvalue x;
    x["msg"] = "Hello, World!";
    return x;
});

Python端的發送消息的代碼如下:

def send_get_json():
    url_get = "http://localhost:8888/json"

    r = requests.get(url_get)
    print(r.text)
    str_json = json.loads(r.text)
    print(str_json["msg"])

在這裏插入圖片描述

1.3 接收json格式消息

  這種情況與1.2的類似,只不過這一次反過來,我們服務端可能會收到很多參數,但是我們只解析我們需要的字段的消息即可,所以就要接收json格式的數據,同樣C++端實現如下:

    CROW_ROUTE(app, "/add_json")
        .methods("POST"_method)
    ([](const crow::request& req){
        auto x = crow::json::load(req.body);
        if (!x)
            return crow::response(400);
        int sum = x["a"].i()+x["b"].i();
        std::ostringstream os;
        os << sum;
        return crow::response{os.str()};
    });

這裏可以看到,返回的消息應該是要設置爲string或者crow支持的json等格式,Python端的發送消息的代碼如下:

def send_num():
    url_get = "http://localhost:8888/add_json"
    data = {"a":1, "b":2}

    r = requests.post(url_get, data=json.dumps(data))
    print(r.text)

1.4 一個綜合的例子

  在這個例子裏:我將用Python讀取一副圖像,然後將圖像數據通過base64編碼,再把編碼後的數據通過json格式數據發送給C++端,C++端接收到消息之後,會對base64數據進行解碼得到圖像數據,然後對圖像進行一些處理,將處理後的圖像通過base64進行編碼,並將編碼的結果返回去,Python端將返回的base64數據進行解碼得到處理過後的圖像。
  C++端會起算法服務,並且需要對base64數據進行編解碼,然後還需要做一些圖像處理的操作:

CROW_ROUTE(app, "/img")
        .methods("POST"_method)
    ([](const crow::request& req){
        auto x = crow::json::load(req.body);
        if (!x)
        {
            return crow::response(400);
        }

        std::string dst_code = x["img"].s();

        std::string dec_jpg = urlsafe_base64_decode(dst_code);
        std::vector<uchar> data(dec_jpg.begin(), dec_jpg.end());
        cv::Mat img = cv::imdecode(cv::Mat(data), 1);

        cv::Mat img_ = 255 - img;
        std::vector<uchar> buf;
        cv::imencode(".jpg", img_, buf);
        auto *enc_msg = reinterpret_cast<unsigned char*>(buf.data());
        std::string encoded = base64_encode(enc_msg, buf.size());

        return crow::response{encoded};
    });

Python需要讀取圖像,並把圖像進行base64的編碼,然後發送,對接受到的數據進行解碼,並顯示圖像,Python端的發送消息的代碼如下:

def image_to_base64(image_np):
    image = cv2.imencode('.jpg',image_np)[1]
    image_code = str(base64.urlsafe_b64encode(image))[2:-1]
    # image_code = str(base64.b64encode(image))[2:-1]

    return image_code

def base64_to_image(base64_code):
    # base64解碼
    # img_data = base64.b64decode(base64_code)
    img_data = base64.urlsafe_b64decode(base64_code)
    # 轉換爲np數組
    img_array = np.fromstring(img_data, np.uint8)
    # 轉換成opencv可用格式
    img = cv2.imdecode(img_array, cv2.COLOR_RGB2BGR)

def send_img():
    url_get = "http://localhost:8888/img"

    mat = cv2.imread("./cat.jpg")

    data_base64 = image_to_base64(mat)
    data = {"img": data_base64}

    r = requests.post(url_get, data=json.dumps(data))
    # print(r.text)

    img = base64_to_image(r.text)
    cv2.imshow("img", img)
    cv2.waitKey(0)

以上是我目前需要用到的功能,後期如果需要用到更多的功能,還會繼續開發這個庫,不過目前基本滿足了我的需要了。

cpp-httplib

  待補充…

耕人扶耒語林丘,花外時時落一鷗。
欲驗春來多少雨,野塘漫水可回舟。
– 宋代·周邦彥 《春雨》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章