Node.js服務(wù)端和Qt C++客戶端之HTTP POST和TCP Socket通信

最近有個需求,需要將Qt/C++客戶端的一些數(shù)據(jù)傳輸至Node.js服務(wù)端,有些需要一次性發(fā)送锨天,另外一些由于更新頻率高预厌,需要建立Socket長鏈接。Google了一下,發(fā)現(xiàn)這方面的資料少得可憐,而自己在摸索的過程中也走了不少彎路,于是把這幾天學(xué)到的東西整理了一下站蝠,以Demo的形式把HTTP POST和TCP Socket通信過程加以實現(xiàn)。

技術(shù)選型

客戶端向服務(wù)端一次性發(fā)送一些數(shù)據(jù)卓鹿,最直接的就是HTTP請求的POST方法了菱魔,而如果需要建立Socket長鏈接,則有很多選擇吟孙。我首先嘗試的是WebSocket澜倦,Node.js有一個很給力的ws模塊,而Qt雖然有WebSockets這個C++類杰妓,但實際應(yīng)用的話是要先斟酌一下的藻治。這一塊本來是個第三方的模塊,后來被Qt給采納了巷挥,所以有版本兼容性和穩(wěn)定性的問題桩卵。還有Socket.IO,是一個面向?qū)崟rweb應(yīng)用的庫倍宾,主要使用WebSocket協(xié)議雏节,最大的特點就是兼容性好,不支持WebSocket的時候自動回退到其他方法凿宾,但應(yīng)用的話服務(wù)端和客戶端都需要使用該框架矾屯,而且它對C++的支持并不是“原生”的兼蕊。最后一個就是TCP Socket了初厚,Node.js有用于底層網(wǎng)絡(luò)通信的內(nèi)置模塊Net提供原生支持,Qt也通過QTcpSocket對其實現(xiàn)了很好的支持孙技。綜上产禾,采用TCP Socket通信成了首選。

Demo

Qt/C++客戶端

在Qt Creator中新建一個項目牵啦,在頭文件中引用需要的系統(tǒng)頭文件:

#include <QBuffer>
#include <QJsonObject>
#include <QJsonDocument>
#include <QJsonArray>
#include <QUrl>
#include <QNetworkRequest>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QTcpSocket>

添加成員變量:

private:
    QNetworkAccessManager* networkManager;
    QTcpSocket *textTcpSocket;
    QTcpSocket *imageTcpSocket;
    QByteArray imageData;

添加槽函數(shù):

private slots:
    void receiveImageTcpSocketMessage();

在源文件的構(gòu)造函數(shù)中亚情,添加如下代碼:

// 初始化
networkManager = new QNetworkAccessManager();
textTcpSocket = new QTcpSocket();
imageTcpSocket = new QTcpSocket();
connect(imageTcpSocket, SIGNAL(readyRead()), this, SLOT(receiveImageTcpSocketMessage()));

// 通過HTTP請求的POST方法發(fā)送一個對象
QJsonObject jsonObjPost;
jsonObjPost.insert("method", "post");
jsonObjPost.insert("message", "biebu.xin");
QByteArray postData = QJsonDocument(jsonObjPost).toJson(QJsonDocument::Compact);
QUrl postUrl("http://127.0.0.1:8080");
QNetworkRequest postRequest(postUrl);
postRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json; charset=utf-8");
networkManager->post(postRequest, postData);

// 通過TCP Socket連接服務(wù)端并發(fā)送一個對象
textTcpSocket->abort();
textTcpSocket->connectToHost("127.0.0.1", 8081);
QJsonObject jsonObjSocket;
jsonObjSocket.insert("method", "socket");
jsonObjSocket.insert("message", "biebu.xin");
QByteArray textData = QJsonDocument(jsonObjSocket).toJson(QJsonDocument::Compact);
textTcpSocket->write(textData);

// 通過TCP Socket連接服務(wù)端并上傳一張圖片
imageTcpSocket->abort();
imageTcpSocket->connectToHost("127.0.0.1", 8082);
QImage image;
image.load("demo.jpg", "JPG");
QBuffer buffer(&imageData);
image.save(&buffer, "JPG");
imageTcpSocket->write(QByteArray::number(imageData.size()));

定義receiveImageTcpSocketMessage()函數(shù):

QByteArray message = imageTcpSocket->readAll();
if (message.toInt()) {
    imageTcpSocket->write(imageData);
    imageData.resize(0);
}

發(fā)送圖片的時候,如果直接把圖片數(shù)據(jù)發(fā)過去哈雏,有可能會出現(xiàn)需要分多次傳輸導(dǎo)致服務(wù)端單次接收數(shù)據(jù)不完整的情況楞件。所以衫生,一種穩(wěn)妥的做法是,先把圖片大小發(fā)過去土浸,然后再發(fā)送圖片數(shù)據(jù)罪针。

Node.js服務(wù)端

新建一個js文件,加載內(nèi)置模塊:

const http = require('http');
const net = require('net');
const fs = require('fs');

創(chuàng)建HTTP服務(wù)器:

http.createServer((req, res) => {
    let source = "";

    req.on('data', function(data) {
        source += data;
    });

    req.on('end', function() {
        console.log(`Post data: ${source.toString()}`);
    });
}).listen(8080, '127.0.0.1', () => {
    console.log('Http server listening on port 8080 at host 127.0.0.1');
});

創(chuàng)建用于接收文本的TCP Socket服務(wù)器:

net.createServer((socket) => {
    console.log('textClient connected');

    socket.on('data', (data) => {
        console.log(`Socket textData: ${data.toString()}`);
    });

    socket.on('end', () => {
        console.log('textClient disconnected');
    });
}).listen(8081, () => {
    console.log('Socket server for text started on port 8081');
});

創(chuàng)建用于接收圖片的TCP Socket服務(wù)器:

net.createServer((socket) => {
    console.log('imageClient connected');

    let imageSize = 0;
    let imageBufferArray = [];

    socket.on('data', (data) => {
        if (imageSize) {
            // 接收和拼接數(shù)據(jù)黄伊,當數(shù)據(jù)長度不夠時泪酱,下一次繼續(xù)
            imageBufferArray.push(data);
            let imageData = Buffer.concat(imageBufferArray);
            if (imageData.byteLength >= imageSize) {
                // 數(shù)據(jù)完整,寫入文件
                fs.writeFile(`demo.jpg`, imageData, (err) => {
                    console.log('Save socket image success');
                    imageSize = 0;
                    imageBufferArray = [];
                });
            }
        } else {
            imageSize = parseInt(data.toString());
            socket.write('1');
        }
    });

    socket.on('end', () => {
        console.log('imageClient disconnected');
    });
}).listen(8082, () => {
    console.log('Socket server for image started on port 8082');
});

效果

服務(wù)端先啟動还最,客戶端運行后墓阀,服務(wù)端的終端中會打印運行結(jié)果:

Socket server for text started on port 8081
Socket server for image started on port 8082
Http server listening on port 8080 at host 127.0.0.1
textClient connected
imageClient connected
Socket textData: {"message":"biebu.xin","method":"socket"}
Post data: {"message":"biebu.xin","method":"post"}
Save socket image success

可以看出,Qt客戶端發(fā)送的數(shù)據(jù)這里都是采用QByteArray類型的拓轻,而Node.js接收的時候斯撮,不管是http模塊,還是net模塊扶叉,其data事件的參數(shù)都是一個Buffer吮成,fs寫入文件的圖片數(shù)據(jù),也是Buffer對象辜梳。注意到這點粱甫,就可以根據(jù)實際需求靈活編程了。

需要強調(diào)的是作瞄,這是個最小Demo茶宵,錯誤處理的部分都省略了,而在實際編程的過程中一定要考慮周全哦~

個人技術(shù)博客 biebu.xin宗挥,原文鏈接——Node.js服務(wù)端和Qt C++客戶端之HTTP POST和TCP Socket通信

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末乌庶,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子契耿,更是在濱河造成了極大的恐慌瞒大,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件搪桂,死亡現(xiàn)場離奇詭異透敌,居然都是意外死亡,警方通過查閱死者的電腦和手機踢械,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門酗电,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人内列,你說我怎么就攤上這事撵术。” “怎么了话瞧?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵嫩与,是天一觀的道長寝姿。 經(jīng)常有香客問我,道長划滋,這世上最難降的妖魔是什么会油? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮古毛,結(jié)果婚禮上翻翩,老公的妹妹穿的比我還像新娘。我一直安慰自己稻薇,他們只是感情好嫂冻,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著塞椎,像睡著了一般桨仿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上案狠,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天服傍,我揣著相機與錄音,去河邊找鬼骂铁。 笑死吹零,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的拉庵。 我是一名探鬼主播灿椅,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼钞支!你這毒婦竟也來了茫蛹?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤烁挟,失蹤者是張志新(化名)和其女友劉穎婴洼,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體撼嗓,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡柬采,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了静稻。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片警没。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡匈辱,死狀恐怖振湾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情亡脸,我是刑警寧澤押搪,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布树酪,位于F島的核電站,受9級特大地震影響大州,放射性物質(zhì)發(fā)生泄漏续语。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一厦画、第九天 我趴在偏房一處隱蔽的房頂上張望疮茄。 院中可真熱鬧,春花似錦根暑、人聲如沸力试。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽畸裳。三九已至,卻和暖如春淳地,著一層夾襖步出監(jiān)牢的瞬間怖糊,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工颇象, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留伍伤,地道東北人。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓遣钳,卻偏偏與公主長得像嚷缭,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子耍贾,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容