C++中實(shí)現(xiàn)WebSocket通信的兩種方法:libwebsockets庫(kù)、Boost.Beast?庫(kù)
概述
WebSocket協(xié)議是現(xiàn)代Web開(kāi)發(fā)中不可或缺的一部分,它允許客戶端和服務(wù)器之間建立持久的連接,實(shí)現(xiàn)雙向?qū)崟r(shí)通信。與傳統(tǒng)的HTTP請(qǐng)求不同,WebSocket提供了一種全雙工的通信通道,使得數(shù)據(jù)可以在任意方向上傳輸,而無(wú)需等待對(duì)方請(qǐng)求或者應(yīng)答。
WebSocket是在HTML5中引入的一種新協(xié)議,旨在替代輪詢等技術(shù)來(lái)實(shí)現(xiàn)客戶端與服務(wù)器間的實(shí)時(shí)交互。它通過(guò)HTTP或HTTPS協(xié)議發(fā)起一個(gè)特殊的請(qǐng)求,一旦連接建立成功,就可以繞過(guò)HTTP協(xié)議直接進(jìn)行數(shù)據(jù)交換。其主要的工作流程可以參考下圖。

握手階段:客戶端發(fā)起一個(gè)HTTP請(qǐng)求,這個(gè)請(qǐng)求與普通的GET請(qǐng)求差不多。但它包含了一些額外的頭字段,比如:Upgrade、Connection、Sec-WebSocket-Key等,表明客戶端希望將現(xiàn)有的TCP連接升級(jí)為WebSocket連接。
連接建立:如果服務(wù)器同意升級(jí),則會(huì)返回一個(gè)狀態(tài)碼為101(Switching Protocols)的響應(yīng),并且同樣帶有Upgrade、Connection等頭字段。
數(shù)據(jù)傳輸:一旦握手完成,雙方就可以開(kāi)始發(fā)送和接收數(shù)據(jù)幀。每個(gè)幀都包含一個(gè)頭部,指示數(shù)據(jù)是否為二進(jìn)制還是文本形式,以及是否為消息的一部分等信息。
C++中WebSocket庫(kù)
cpp-websocket:這是一個(gè)C++編寫的WebSocket庫(kù),提供了簡(jiǎn)單易用的API,支持WebSocket握手、消息傳輸和關(guān)閉連接等功能。cpp-websocket的代碼量相對(duì)較小,易于集成到項(xiàng)目中。
asio_websocket:基于Boost.Asio庫(kù)的一個(gè)C++ WebSocket庫(kù)。Boost.Asio是一個(gè)廣泛使用的C++網(wǎng)絡(luò)編程庫(kù),提供了異步I/O操作、事件驅(qū)動(dòng)編程等功能。asio_websocket繼承了Boost.Asio的高性能和靈活性,同時(shí)提供了WebSocket協(xié)議的實(shí)現(xiàn)1。
websockets++:這是一個(gè)C++11編寫的輕量級(jí)WebSocket庫(kù),支持WebSocket握手、消息傳輸和關(guān)閉連接等功能。websockets++提供了簡(jiǎn)潔的API和豐富的文檔,方便開(kāi)發(fā)者學(xué)習(xí)和使用。
websocketpp:這是一個(gè)功能豐富的C++ WebSocket庫(kù),支持多種WebSocket協(xié)議版本,適用于復(fù)雜的WebSocket通信需求。websocketpp是跨平臺(tái)的開(kāi)源庫(kù),支持事件驅(qū)動(dòng)接口和靈活的依賴管理。
libwebsockets:這是一個(gè)跨平臺(tái)的C語(yǔ)言庫(kù),支持C++綁定,提供了靈活的WebSocket通信解決方案。libwebsockets適合對(duì)性能有高要求的場(chǎng)景。
uWebSockets:這是一個(gè)簡(jiǎn)單、高效且輕量級(jí)的WebSocket和HTTP實(shí)現(xiàn),底層依賴于libuv庫(kù)。uWebSockets非常適合需要處理大量并發(fā)連接的場(chǎng)景,盡管它在某些平臺(tái)或環(huán)境中的穩(wěn)定性和成熟度可能不如其他庫(kù)。
Boost.Beast:這是一個(gè)基于Boost庫(kù)的WebSocket庫(kù),提供了高性能和易用的API來(lái)實(shí)現(xiàn)WebSocket通信。Boost.Beast適用于需要高性能和易用性的場(chǎng)景。
Simple-WebSocket-Server:這是一個(gè)輕量級(jí)的WebSocket庫(kù),適用于簡(jiǎn)單的WebSocket通信需求。
libwebsockets庫(kù)
libwebsockets是一個(gè)開(kāi)源的C語(yǔ)言庫(kù),用于實(shí)現(xiàn)WebSocket和HTTP服務(wù)器及客戶端。它非常靈活,支持多種協(xié)議,適合用于開(kāi)發(fā)高性能的應(yīng)用程序和服務(wù)。libwebsockets的主要功能如下:
1、WebSocket服務(wù)器和客戶端:支持WebSocket協(xié)議的完整實(shí)現(xiàn),可以創(chuàng)建WebSocket服務(wù)器和客戶端。
2、HTTP服務(wù)器:支持HTTP/1.x和HTTP/2協(xié)議,可以創(chuàng)建高性能的HTTP服務(wù)器。
3、TLS/SSL支持:支持使用OpenSSL進(jìn)行加密,確保通信的安全性。
4、事件驅(qū)動(dòng)模型:采用非阻塞的I/O模型,適合處理大量并發(fā)連接。
5、數(shù)據(jù)壓縮:支持使用zlib進(jìn)行數(shù)據(jù)壓縮,減少帶寬消耗。
6、自定義協(xié)議:允許用戶定義自己的協(xié)議,而不僅僅限于WebSocket和HTTP。
使用libwebsockets庫(kù)開(kāi)發(fā)WebSocket客戶端主要有六步,其工作流程可以參考下圖。

1、初始化庫(kù)和上下文。首先需要初始化libwebsockets庫(kù),使用lws_create_context函數(shù)創(chuàng)建一個(gè)上下文對(duì)象,該對(duì)象包含了庫(kù)運(yùn)行所需的配置信息。
2、創(chuàng)建連接。使用lws_client_connect函數(shù)創(chuàng)建一個(gè)WebSocket連接到指定的服務(wù)器,服務(wù)器的URL由調(diào)用方給出。
3、注冊(cè)回調(diào)函數(shù)。為了處理連接的不同階段,我們需要注冊(cè)回調(diào)函數(shù)。這些回調(diào)函數(shù)會(huì)在特定的事件發(fā)生時(shí)被調(diào)用,比如:連接建立、連接斷開(kāi)、數(shù)據(jù)接收等。一般在lws_context_creation_info結(jié)構(gòu)體的callbacks字段中,可以指定回調(diào)函數(shù)。
4、發(fā)送和接收數(shù)據(jù)。一旦連接建立,我們便可以開(kāi)始發(fā)送和接收數(shù)據(jù)。發(fā)送數(shù)據(jù)通常在LWS_CALLBACK_CLIENT_WRITEABLE回調(diào)中進(jìn)行,使用lws_send或lws_write函數(shù),而接收數(shù)據(jù)則在LWS_CALLBACK_CLIENT_RECEIVE回調(diào)中進(jìn)行。
5、處理事件循環(huán)。libwebsockets庫(kù)使用事件循環(huán)來(lái)處理網(wǎng)絡(luò)I/O操作,我們需要在一個(gè)無(wú)限循環(huán)中調(diào)用lws_service函數(shù)來(lái)處理事件。
6、清理資源。當(dāng)不再需要連接時(shí),應(yīng)該釋放所有由libwebsockets分配的資源,通常通過(guò)調(diào)用lws_context_destroy函數(shù)來(lái)銷毀上下文。
根據(jù)上面的工作流程,我們不難寫出WebSocket客戶端的示例代碼,具體如下。
#include <libwebsockets.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define WEB_SOCKET_MSG "Hello From Hope Wisdom"
static lws_context *s_pContext = nullptr;
// WebSocket客戶端回調(diào)函數(shù)
static int OnCallbackWebSocket(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
switch(reason)
{
case LWS_CALLBACK_CLIENT_ESTABLISHED:
// 連接已建立
printf("Connected!\n");
break;
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
// 連接錯(cuò)誤
printf("Connection error: %s\n", (char *)in);
break;
case LWS_CALLBACK_CLIENT_CLOSED:
// 連接關(guān)閉
printf("Disconnected.\n");
break;
case LWS_CALLBACK_CLIENT_RECEIVE:
// 接收數(shù)據(jù)
printf("Received: %.*s\n", (int)len, (const char *)in);
break;
case LWS_CALLBACK_CLIENT_WRITEABLE:
// 當(dāng)連接可寫時(shí),可以發(fā)送數(shù)據(jù)
if (lws_send(wsi, WEB_SOCKET_MSG, strlen(WEB_SOCKET_MSG), LWS_SEND_TEXT) < 0)
{
printf("Send failed!\n");
}
break;
default:
break;
}
return 0;
}
static int init_lws()
{
struct lws_context_creation_info info;
lws_zeroize(&info, sizeof(info));
info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | LWS_SERVER_OPTION_VALIDATE_UTF8;
// 設(shè)置回調(diào)函數(shù)
info.callbacks = &OnCallbackWebSocket;
// 設(shè)置端口(客戶端不需要監(jiān)聽(tīng)端口)
info.port = LWS_NO_LISTEN;
info.name = "Client";
// 創(chuàng)建上下文
s_pContext = lws_create_context(&info);
if (!s_pContext)
{
printf("Failed to create context\n");
return -1;
}
return 0;
}
static int connect_to_server(const char *uri)
{
struct lws *wsi = nullptr;
wsi = lws_client_connect(s_pContext, uri, nullptr, LWS_CLIENT_CONNECT);
if (!wsi)
{
printf("Failed to connect to %s\n", uri);
return -1;
}
return 0;
}
int main(int argc, char **argv)
{
if (argc < 2)
{
printf("Usage: %s <websocket-uri>\n", argv[0]);
return -1;
}
// 初始化libwebsockets庫(kù)
if (init_lws() < 0)
{
return -1;
}
// 連接到WebSocket服務(wù)器
if (connect_to_server(argv[1]) < 0)
{
return -1;
}
// 主事件循環(huán)
while (lws_service(s_pContext, 0))
{
// 自動(dòng)處理連接的讀寫
}
// 釋放資源
lws_context_destroy(s_pContext);
return 0;
}Boost.Beast擴(kuò)展
Boost.Beast,可簡(jiǎn)稱為Beast,是Boost庫(kù)中的一個(gè)子項(xiàng)目。它是一個(gè)現(xiàn)代C++網(wǎng)絡(luò)庫(kù),主要用于構(gòu)建高性能的網(wǎng)絡(luò)應(yīng)用程序和服務(wù)。Beast是專門為C++11及更高版本設(shè)計(jì)的,它利用了現(xiàn)代C++語(yǔ)言的特性,比如:移動(dòng)語(yǔ)義、智能指針等,以提高軟件整體性能。
Beast的主要組件包括:基礎(chǔ)的網(wǎng)絡(luò)抽象(比如:Stream、Buffer)、HTTP(包括:HTTP/1.x、HTTP/2)、WebSockets等。其主要特點(diǎn)如下。
1、基于Boost.Asio。Beast是基于Boost.Asio的高級(jí)網(wǎng)絡(luò)庫(kù),因此它可以無(wú)縫集成到任何使用Boost.Asio的項(xiàng)目中。Beast提供了異步IO模型,允許非阻塞的操作,這對(duì)于構(gòu)建高并發(fā)的網(wǎng)絡(luò)服務(wù)非常重要。
2、高性能。設(shè)計(jì)用于高性能的應(yīng)用場(chǎng)景,支持高效的內(nèi)存管理,避免不必要的內(nèi)存拷貝。提供了零拷貝機(jī)制,即在傳輸數(shù)據(jù)時(shí)不進(jìn)行內(nèi)存復(fù)制,而是直接使用原始數(shù)據(jù)緩沖區(qū)。
3、易于使用。提供了簡(jiǎn)潔的API,使得編寫網(wǎng)絡(luò)應(yīng)用程序變得更加直觀和容易。支持錯(cuò)誤處理機(jī)制,使得捕獲和處理網(wǎng)絡(luò)錯(cuò)誤變得更加容易。
4、協(xié)議支持。支持多種網(wǎng)絡(luò)協(xié)議,包括:HTTP/1.x、HTTP/2、WebSockets等。另外,還提供了HTTP和WebSocket的客戶端和服務(wù)器實(shí)現(xiàn)。
5、跨平臺(tái)。Beast可以在多個(gè)操作系統(tǒng)上運(yùn)行,包括:Windows、Linux、MacOS等。它具有良好的跨平臺(tái)兼容性,使得開(kāi)發(fā)者可以編寫一次代碼并在不同平臺(tái)上運(yùn)行。
使用Beast庫(kù)編寫WebSocket客戶端比較簡(jiǎn)單,示例代碼如下。
#include <boost/beast.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <string>
using namespace std;
namespace beast = boost::beast;
namespace http = beast::http;
namespace net = boost::asio;
using tcp = net::ip::tcp;
int main()
{
try {
// 創(chuàng)建I/O上下文和解析器
net::io_context ioc;
tcp::resolver resolver(ioc);
// 解析服務(wù)器地址和端口
auto endpoints = resolver.resolve("echo.websocket.org", "80");
// 創(chuàng)建并連接socket
tcp::socket socket(ioc);
beast::get_lowest_layer(socket).connect(endpoints);
// 設(shè)置HTTP請(qǐng)求
http::request<http::string_body> req{http::verb::get, "/", 11};
req.set(http::field::host, "echo.websocket.org");
req.set(http::field::upgrade, "websocket");
req.set(http::field::connection, "Upgrade");
// 隨機(jī)的Base64編碼的WebSocket密鑰
req.set(http::field::sec_websocket_key, "dGhlIHNhbXBsZSBub25Nl29rZXk=");
req.set(http::field::sec_websocket_version, "13");
// 使用websocket::stream進(jìn)行握手
websocket::stream<tcp::socket> ws{ioc};
ws.assign(std::move(socket));
// 發(fā)送HTTP升級(jí)請(qǐng)求并接收響應(yīng)來(lái)完成握手
http::response<http::string_body> res;
ws.handshake(req, res);
// 發(fā)送消息到服務(wù)器
beast::error_code ec;
ws.write(net::buffer("Hello, Hope Wisdom"), ec);
// 接收消息
while (true)
{
beast::flat_buffer receive_buffer;
websocket::opcode op;
ws.read(op, receive_buffer);
ws.consume(receive_buffer.data().size());
if (op == websocket::opcode::close)
{
break;
}
cout << "Received: " << beast::buffers_to_string(receive_buffer.data()) << endl;
}
// 關(guān)閉連接
ws.close(websocket::close_code::normal);
}
catch (exception const& e)
{
cerr << "Error: " << e.what() << endl;
}
return 0;
}在上面的示例代碼中,我們首先創(chuàng)建了一個(gè)I/O上下文io_context和一個(gè)DNS解析器resolver,解析了服務(wù)器的地址和端口。接著,我們創(chuàng)建了一個(gè)TCP套接字,并使用解析結(jié)果中的端點(diǎn)列表連接到服務(wù)器。隨后,設(shè)置了HTTP請(qǐng)求,以發(fā)起WebSocket的握手請(qǐng)求,其中包括了必要的頭部信息,比如:Upgrade、Connection、Sec-WebSocket-Key、Sec-WebSocket-Version。這些頭部信息用于告知服務(wù)器希望將連接升級(jí)為WebSocket連接,并提供了握手所需的密鑰和版本號(hào)。
為了執(zhí)行WebSocket握手,我們創(chuàng)建了一個(gè)stream<socket>對(duì)象ws,并將之前的TCP套接字socket賦值給它。然后,調(diào)用ws.handshake函數(shù)來(lái)發(fā)送HTTP升級(jí)請(qǐng)求,并接收響應(yīng)以完成握手過(guò)程。
握手完成后,我們通過(guò)ws.write函數(shù)發(fā)送了一條文本消息到服務(wù)器。隨后,進(jìn)入一個(gè)無(wú)限循環(huán),不斷接收來(lái)自服務(wù)器的消息。每次接收到消息后,都會(huì)檢查是否為關(guān)閉幀。如果是,則退出循環(huán)。否則,打印接收到的消息。
最后,在退出循環(huán)時(shí),我們會(huì)釋放資源,關(guān)閉WebSocket連接和TCP套接字。
總結(jié)
C++中WebSocket庫(kù)主要有以下幾個(gè):cpp-websocket、asio_websocket、websockets++、websocketpp、libwebsockets、uWebSockets、Boost.Beast、Simple-WebSocket-Server,這篇文章使用libwebsockets庫(kù)、Boost.Beast庫(kù)來(lái)實(shí)現(xiàn)c++中的WebSocket通信。
到此這篇關(guān)于C++中實(shí)現(xiàn)WebSocket通信的兩種方法:libwebsockets庫(kù)、Boost.Beast庫(kù)的文章就介紹到這了,更多相關(guān)C++中WebSocket通信內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
opencv實(shí)現(xiàn)機(jī)器視覺(jué)檢測(cè)和計(jì)數(shù)的方法
在機(jī)器視覺(jué)中,有時(shí)需要對(duì)產(chǎn)品進(jìn)行檢測(cè)和計(jì)數(shù)。其難點(diǎn)無(wú)非是對(duì)于產(chǎn)品的圖像分割。本文就來(lái)介紹一下機(jī)器視覺(jué)檢測(cè)和計(jì)數(shù)的實(shí)現(xiàn),感興趣的可以參考一下2021-05-05
C++實(shí)現(xiàn)簡(jiǎn)單的圖書管理系統(tǒng)
本文給大家分享的是使用C++實(shí)現(xiàn)簡(jiǎn)單的圖書管理系統(tǒng)的代碼,本系統(tǒng)采用了面向?qū)ο蟮某绦蛟O(shè)計(jì)方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2015-08-08
c++實(shí)現(xiàn)獲取當(dāng)前時(shí)間(精確至秒,毫秒和微妙)
這篇文章主要為大家詳細(xì)介紹了c++實(shí)現(xiàn)獲取當(dāng)前時(shí)間(可以精確至秒,毫秒和微妙)的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以參考一下2023-11-11
關(guān)于C++靜態(tài)數(shù)據(jù)成員的實(shí)現(xiàn)講解
今天小編就為大家分享一篇關(guān)于關(guān)于C++靜態(tài)數(shù)據(jù)成員的實(shí)現(xiàn)講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12

