1、背景介紹
1.1 WebSocket介紹
WebSocket 是 HTML5 開(kāi)始提供的一種在單個(gè) TCP 連接上進(jìn)行全雙工通訊的協(xié)議。
WebSocket 使得客戶端和服務(wù)器之間的數(shù)據(jù)交換變得更加簡(jiǎn)單,允許服務(wù)端主動(dòng)向客戶端推送數(shù)據(jù)。在 WebSocket API 中,瀏覽器和服務(wù)器只需要完成一次握手,兩者之間就直接可以創(chuàng)建持久性的連接,并進(jìn)行雙向數(shù)據(jù)傳輸。
在 WebSocket API 中,瀏覽器和服務(wù)器只需要做一個(gè)握手的動(dòng)作,然后,瀏覽器和服務(wù)器之間就形成了一條快速通道。兩者之間就直接可以數(shù)據(jù)互相傳送。
1.2 WebSocket 屬性
連接狀態(tài)屬性
以下是 WebSocket 對(duì)象的屬性:
0 - 表示連接尚未建立。
1 - 表示連接已建立,可以進(jìn)行通信。
2 - 表示連接正在進(jìn)行關(guān)閉。
3 - 表示連接已經(jīng)關(guān)閉或者連接不能打開(kāi)。
Socket.bufferedAmount
只讀屬性 bufferedAmount 為T(mén)rue表示信息已被 send() 放入正在隊(duì)列中等待傳輸,但是還沒(méi)有發(fā)出的 UTF-8 文本字節(jié)數(shù)。
WebSocket 事件
以下是 WebSocket 對(duì)象的相關(guān)事件:
事件 事件處理程序 描述
open Socket.onopen 連接建立時(shí)觸發(fā)
message Socket.onmessage 客戶端接收服務(wù)端數(shù)據(jù)時(shí)觸發(fā)
error Socket.onerror 通信發(fā)生錯(cuò)誤時(shí)觸發(fā)
close Socket.onclose 連接關(guān)閉時(shí)觸發(fā)
WebSocket 方法
以下是 WebSocket 對(duì)象的相關(guān)方法,用于信息發(fā)生與連接
方法 描述
Socket.send() //使用連接發(fā)送數(shù)據(jù)
Socket.close() //關(guān)閉連接
1.3 easywsclient介紹
websocket并不局限于在網(wǎng)頁(yè)端(JS客戶端使用),其還支持在各個(gè)后端中使用,如C++、Java等
easywsclient是一個(gè)基于C++11的輕量化標(biāo)注websocket客戶端庫(kù)(跨平臺(tái),支持各種系統(tǒng)),支持RFC 6455版本13 WebSocket,兼容現(xiàn)行所有的WebSocket服務(wù)器(如c++ crow庫(kù)的WebSocket服務(wù)端)。對(duì)于socketio的服務(wù)器兼容存在問(wèn)題,應(yīng)使用socketio的客戶端。
項(xiàng)目地址: https://github.com/dhbaird/easywsclient
2、easywsclient使用
2.1 代碼介紹
easywsclient項(xiàng)目核心代碼為"easywsclient.hpp"和"easywsclient.cpp",僅需將這兩個(gè)文件拷貝到自己項(xiàng)目下即可使用WebSocket客戶端通信了。
其關(guān)鍵接口函數(shù)有以下4個(gè),send函數(shù)用于發(fā)送文本信息,sendBinary函數(shù)用于發(fā)送二進(jìn)制信息,dispatch用于設(shè)置信息接收處理的函數(shù)
ws->poll();
ws->send("hello");
//ws->sendBinary(vp);
ws->dispatch(handle_message);
2.2 基本使用
使用easywsclient主要是要指定dispatch函數(shù),可以在dispatch函數(shù)中對(duì)服務(wù)器返回的信息進(jìn)行處理,也可以在此處斷開(kāi)WebSocket連接
#include "easywsclient.hpp"
//#include "easywsclient.cpp" // <-- include only if you don't want compile separately
using easywsclient::WebSocket;
WebSocket::pointer ws;
void handle_message_close(const std::string& message)
{
ws->close();
}
int
main()
{
#ifdef _WIN32
INT rc;
WSADATA wsaData;
rc = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (rc) {
printf("WSAStartup Failed.\n");
return 1;
}
#endif
ws = WebSocket::from_url("ws://localhost:8126/foo");
assert(ws);
while (websocketclient->getReadyState() != WebSocket::CLOSED) {
ws->poll();
ws->send("hello");
ws->dispatch(handle_message_close);
}
...
delete ws; // alternatively, use unique_ptr<> if you have C++11
#ifdef _WIN32
WSACleanup();
#endif
return 0;
}
2.3 發(fā)送二進(jìn)制數(shù)據(jù)
需要調(diào)用sendBinary函數(shù),其入?yún)閟td::vector<uint8_t>類型。其支持將多種數(shù)據(jù)類型進(jìn)行拼接,允許一次性傳入多個(gè)數(shù)據(jù)(使用memcpy將數(shù)據(jù)復(fù)制到對(duì)應(yīng)內(nèi)存位置即可)。
long dsize = sizeof(int) +sizeof(char) * txt_data.size() ;
uint8_t* buffer;
buffer = (uint8_t*)malloc(sizeof(uint8_t) * dsize);
memcpy(buffer, 123456, sizeof(int));
memcpy(buffer+sizeof(int), txt_data.data(), sizeof(char)*txt_data.size());
std::vector<uint8_t> vp(buffer, buffer + dsize);
ws->poll();
ws->sendBinary(vp);
//可以將二進(jìn)制數(shù)據(jù)保存下來(lái),與服務(wù)器端進(jìn)行驗(yàn)證
ofstream fout("send.dat", ios::binary);
fout.write(reinterpret_cast<const char*>(buffer), sizeof(uint8_t) * dsize);
fout.close();
3、websocket服務(wù)器
在c++下搭建websocket服務(wù)器需要依賴其他的第三方庫(kù),這里初略介紹一下2個(gè)可以實(shí)現(xiàn)websocket服務(wù)器的c++庫(kù)
3.1 使用websocketpp庫(kù)
websocketpp庫(kù)下載地址為:https://github.com/zaphoyd/websocketpp/,其依賴Boost庫(kù)。故此,在導(dǎo)入websocketpp庫(kù)前,需要先導(dǎo)入Boost庫(kù)。websocketpp庫(kù)也支持websocket客戶端,但考慮到依賴的庫(kù)更多了,還是推薦使用easywsclient庫(kù)
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
#include <iostream>
typedef websocketpp::server<websocketpp::config::asio> server;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
// pull out the type of messages sent by our config
typedef server::message_ptr message_ptr;
// Define a callback to handle incoming messages
void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) {
std::string cmd = msg->get_payload();
std::string res = "fu wu qi de xin xi! \" ";
std::cout << "on_message called with hdl: " << hdl.lock().get()
<< " and message: " << msg->get_payload()
<< std::endl;
// check for a special command to instruct the server to stop listening so
// it can be cleanly exited.
if (msg->get_payload() == "stop-listening") {
s->stop_listening();
return;
}
try {
s->send(hdl, res, msg->get_opcode());
} catch (websocketpp::exception const & e) {
std::cout << "Echo failed because: "
<< "(" << e.what() << ")" << std::endl;
}
}
int main() {
// Create a server endpoint
server echo_server;
try {
// Set logging settings
echo_server.set_access_channels(websocketpp::log::alevel::all);
echo_server.clear_access_channels(websocketpp::log::alevel::frame_payload);
// Initialize Asio
echo_server.init_asio();
// Register our message handler
echo_server.set_message_handler(bind(&on_message,&echo_server,::_1,::_2));
// Listen on port 9002
echo_server.listen(9002);
// Start the server accept loop
echo_server.start_accept();
// Start the ASIO io_service run loop
echo_server.run();
} catch (websocketpp::exception const & e) {
std::cout << e.what() << std::endl;
} catch (...) {
std::cout << "other exception" << std::endl;
}
}
詳情可以參考:https://hpg123.blog.csdn.net/article/details/124888325文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-651932.html
3.2 使用crow庫(kù)
corw是一個(gè)開(kāi)源、輕量化的c++web庫(kù),在使用上與python的flask是類似的,其支持路由綁定、返回?cái)?shù)據(jù)(json、文本、response對(duì)象(靜態(tài)資源、模板文件)、接口請(qǐng)求處理(REST請(qǐng)求,url參數(shù)綁定、json請(qǐng)求、GET參數(shù)和POST
參數(shù))和各種高級(jí)操作(Cookie操作、Session操作、文件上傳操作、文件下載操作、websocket
操作、自定義loghandler)。此外,還對(duì)各類參數(shù)請(qǐng)求、結(jié)果返回過(guò)程中對(duì)中文的支持(如get
參數(shù)、post參數(shù)、url參數(shù)、json結(jié)果中中文參數(shù)的正確解讀)。其使用文檔的下載地址為:https://download.csdn.net/download/a486259/87471152文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-651932.html
#include "crow.h"
#include <unordered_set>
#include <mutex>
int main()
{
crow::SimpleApp app;
std::mutex mtx;
std::unordered_set<crow::websocket::connection*> users;
CROW_WEBSOCKET_ROUTE(app, "/ws")
.onopen([&](crow::websocket::connection& conn) {
CROW_LOG_INFO << "new websocket connection from " << conn.get_remote_ip();
std::lock_guard<std::mutex> _(mtx);
users.insert(&conn);
})
.onclose([&](crow::websocket::connection& conn, const std::string& reason) {
CROW_LOG_INFO << "websocket connection closed: " << reason;
std::lock_guard<std::mutex> _(mtx);
users.erase(&conn);
})
.onmessage([&](crow::websocket::connection& /*conn*/, const std::string& data, bool is_binary){
std::lock_guard<std::mutex> _(mtx);
//給當(dāng)前用戶發(fā)信息
conn.send_text(data);
//給所有用戶發(fā)信息
for (auto u : users){
if (is_binary){
u->send_binary(data);
}else{
u->send_text(data);
});
CROW_ROUTE(app, "/")
([] {
char name[256];
gethostname(name, 256);
crow::mustache::context x;
x["servername"] = name;
auto page = crow::mustache::load("ws.html");
return page.render(x);
});
}
到了這里,關(guān)于C++下輕量化websocket客戶端庫(kù)——easywsclient的使用的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!