使用linux c开源库libwebsockets编写的websocket客户端
                                                            生活随笔
收集整理的這篇文章主要介紹了
                                使用linux c开源库libwebsockets编写的websocket客户端
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.                        
                                <一>: 背景
19年中旬做的一個嵌入式項目, 應用層需要有一個心跳的功能,?當時決定用websocket協議, 所以當時就研究了下libwebsockets的使用. 網上的資料并不多, 當時只能研究開源庫提供的測試代碼, 然后改改后就合并到項目里了. 這幾天又把libwebsockets重新編譯了下, 順手重新寫了一個簡單的websocket客戶端. 主要包含了以下三個功能:
1): 定時發送心跳數據;
2): 掉網重連;
3): 是否使用ssl;
代碼如<二>, 不足之處請多指教.
<二>: 代碼
WebsocketClient.h如下:
#ifndef WEBSOCKET_CLIENT_H #define WEBSOCKET_CLIENT_H#include <string.h> #include <libwebsockets.h>#define _THIS_ "this" #define _ADDR_ "addr" #define _PORT_ "port" #define _PATH_ "path" #define _SSL_ "ssl"class WebsocketClient; typedef struct ClientData {struct lws_context *context;struct lws_vhost *vhost;struct lws *client_wsi;const char **addr;int *port;const char **path;int *ssl;WebsocketClient *wsc; }_ClientData;class WebsocketClient { public:WebsocketClient();~WebsocketClient(){if (mContext != NULL){lws_context_destroy(mContext);mContext = NULL;}}public:bool Start(int argc, const char **argv);private:bool init(int argc, const char **argv);public:char msg[1024];private:struct lws_context *mContext;struct lws_protocol_vhost_options lpvoAddr; // 地址struct lws_protocol_vhost_options lpvoPort; // 端口struct lws_protocol_vhost_options lpvoPath; // 路徑struct lws_protocol_vhost_options lpvoSSL; // 是否使用sslstruct lws_protocol_vhost_options lpvoThis; // 傳遞的本類struct lws_protocol_vhost_options lpvo; };#endif // WEBSOCKET_CLIENT_HWebsocketClient.cpp如下:
#include "WebsocketClient.h"static const char *addr = "localhost", *path = "/"; static int port = 7681, ssl = 0;static int callBackFunc(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len); #define WEBSOCKET_CLIENT \ { \"websocket-client", \callBackFunc, \0, \1024, \0, NULL, 0 \ } static struct lws_protocols lProtocols[] = {WEBSOCKET_CLIENT,{ NULL, NULL, 0, 0 }};// 用于事件回調 static void scheduleCallBack(struct lws *wsi, int reason, int secs) {lws_timed_callback_vh_protocol(lws_get_vhost(wsi), lws_get_protocol(wsi), reason, secs); }// 連接服務 static int connectServer(_ClientData *ClientData) {struct lws_client_connect_info lccInfo;memset(&lccInfo, 0, sizeof(lccInfo));char host[32];lws_snprintf(host, sizeof(host), "%s:%u", *ClientData->addr, *ClientData->port);lccInfo.context = ClientData->context;lccInfo.port = *ClientData->port;lccInfo.address = *ClientData->addr;lccInfo.path = *ClientData->path;lccInfo.host = host;lccInfo.origin = host;lccInfo.ssl_connection = *ClientData->ssl;lccInfo.vhost = ClientData->vhost;lccInfo.pwsi = &ClientData->client_wsi;lwsl_user("connecting to ws://%s:%d%s\n", lccInfo.address, lccInfo.port, lccInfo.path);return !lws_client_connect_via_info(&lccInfo); }// lws_service回調 static int callBackFunc(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) {_ClientData *clientData = (_ClientData *)lws_protocol_vh_priv_get(lws_get_vhost(wsi), lws_get_protocol(wsi));switch (reason) {case LWS_CALLBACK_PROTOCOL_INIT:lwsl_user("LWS_CALLBACK_PROTOCOL_INIT\n");clientData = (_ClientData *)lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), lws_get_protocol(wsi), sizeof(_ClientData));if (!clientData)return -1;clientData->context = lws_get_context(wsi);clientData->vhost = lws_get_vhost(wsi);clientData->port = (int *)lws_pvo_search((const struct lws_protocol_vhost_options *)in, _PORT_)->value;clientData->addr = (const char **)lws_pvo_search((const struct lws_protocol_vhost_options *)in, _ADDR_)->value;clientData->path = (const char **)lws_pvo_search((const struct lws_protocol_vhost_options *)in, _PATH_)->value;clientData->ssl = (int *)lws_pvo_search((const struct lws_protocol_vhost_options *)in, _SSL_)->value;char *pptr;pptr = (char *)lws_pvo_search((const struct lws_protocol_vhost_options *)in, _THIS_)->value;clientData->wsc = reinterpret_cast<WebsocketClient *>(pptr);if (connectServer(clientData))scheduleCallBack(wsi, LWS_CALLBACK_USER, 1);break;// 連接創建成功事件, 會調用: LWS_CALLBACK_TIMERcase LWS_CALLBACK_CLIENT_ESTABLISHED:lwsl_user("LWS_CALLBACK_CLIENT_ESTABLISHED\n");lws_set_timer_usecs(wsi, 5 * LWS_USEC_PER_SEC);break;// 寫事件case LWS_CALLBACK_CLIENT_WRITEABLE:char *tmp;int sendLen, retLen;tmp = (char *)"{\"ping\":1,\"msg\":\"ping msg\"}";sendLen = strlen(tmp);char sendMsg[LWS_PRE + 1024];memcpy(sendMsg + LWS_PRE, tmp, sendLen);retLen = lws_write(wsi, ((unsigned char *)sendMsg) + LWS_PRE, sendLen, LWS_WRITE_TEXT);if (retLen < sendLen) {lwsl_err("sendMsg error\n");return -1;}lwsl_user("LWS_CALLBACK_CLIENT_WRITEABLE: %d, sendMsg: %s, sendLen: %d\n", reason, tmp, sendLen);break;// 收事件case LWS_CALLBACK_CLIENT_RECEIVE:lwsl_user("LWS_CALLBACK_CLIENT_RECEIVE: %d, receiveMsg: %s, receiveLen: %d\n", reason, in, len);memcpy(clientData->wsc->msg, (char *)in, len);lwsl_user("clientData->wsc->msg: %s\n", clientData->wsc->msg);break;// 連接錯誤事件, 會調用: LWS_CALLBACK_USERcase LWS_CALLBACK_CLIENT_CONNECTION_ERROR:lwsl_err("CLIENT_CONNECTION_ERROR\n");scheduleCallBack(wsi, LWS_CALLBACK_USER, 1);break;// 時間事件, 會調用: LWS_CALLBACK_CLIENT_WRITEABLEcase LWS_CALLBACK_TIMER:lws_callback_on_writable(wsi);lws_set_timer_usecs(wsi, 5 * LWS_USEC_PER_SEC);break;// 客戶端session關閉事件case LWS_CALLBACK_CLIENT_CLOSED:lwsl_user("LWS_CALLBACK_CLIENT_CLOSED\n");lws_cancel_service(lws_get_context(wsi));scheduleCallBack(wsi, LWS_CALLBACK_USER, 1);break;// 自定義事件, 當前用于重連case LWS_CALLBACK_USER:lwsl_notice("%s: LWS_CALLBACK_USER\n", __func__);if (connectServer(clientData))scheduleCallBack(wsi, LWS_CALLBACK_USER, 1);break;default:break;}return 0; }WebsocketClient::WebsocketClient() {memset(msg, 0x00, sizeof(msg));lpvoAddr = {NULL, NULL, _ADDR_, (char *)((void *)&addr)};lpvoPort = {&lpvoAddr, NULL, _PORT_, (char *)((void *)&port)};lpvoPath = {&lpvoPort, NULL, _PATH_, (char *)((void *)&path)};lpvoSSL = {&lpvoPath, NULL, _SSL_, (char *)((void *)&ssl)};char *tmp = reinterpret_cast<char *>(this);lpvoThis = {&lpvoSSL, NULL, _THIS_, (char *)((void *)tmp)};lpvo = {NULL, &lpvoThis, "websocket-client", ""}; }bool WebsocketClient::init(int argc, const char **argv) {struct lws_context_creation_info lcrInfo;memset(&lcrInfo, 0, sizeof lcrInfo);const char *tmpPtr = NULL;int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;// 連接地址if ((tmpPtr = lws_cmdline_option(argc, argv, "--addr")))addr = tmpPtr;// 連接端口if ((tmpPtr = lws_cmdline_option(argc, argv, "--port")))port = atoi(tmpPtr);// 虛擬路徑if ((tmpPtr = lws_cmdline_option(argc, argv, "--path")))path = tmpPtr;// 是否使用sslif ((tmpPtr = lws_cmdline_option(argc, argv, "--ssl")))ssl = atoi(tmpPtr);if (ssl){if ((tmpPtr = lws_cmdline_option(argc, argv, "--ca_path"))){lcrInfo.client_ssl_ca_filepath = tmpPtr; } else {lwsl_err("cannot find ca_path\n");return false;}}// 日志輸出等級if ((tmpPtr = lws_cmdline_option(argc, argv, "--log_level")))logs = atoi(tmpPtr);lws_set_log_level(logs, NULL);lcrInfo.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */lcrInfo.protocols = lProtocols;lcrInfo.pvo = &lpvo;lcrInfo.pt_serv_buf_size = 32 * 1024;lcrInfo.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | LWS_SERVER_OPTION_VALIDATE_UTF8;lcrInfo.fd_limit_per_thread = 1 + 1 + 1;mContext = lws_create_context(&lcrInfo);if (!mContext) {lwsl_err("lws init failed\n");return false;}return true; }bool WebsocketClient::Start(int argc, const char **argv) {if (!init(argc, argv)){return false;}while (!lws_service(mContext, 0));lws_context_destroy(mContext);return true; }測試文件main.cpp如下:
#include "WebsocketClient.h"int main(int argc, const char **argv) {WebsocketClient websocketClient;if (!websocketClient.Start(argc, argv)){return -1;}return 0; }編譯命令如下:
g++ main.cpp WebsocketClient.cpp -o main -lwebsockets -Wall -std=c++11
<三>: 測試
打開一個websocket服務, 如果沒有, 可以根據上篇文章編譯一個echo服務. 本測試就用自己編譯的echo服務, 結果如下:
結束:
? ? 如果有什么問題, 大家可以一起討論解決.
總結
以上是生活随笔為你收集整理的使用linux c开源库libwebsockets编写的websocket客户端的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 一组数字随机排序
- 下一篇: 架构漫谈(一):什么是架构? -王概凯
