封装利用libwebsockets写出的客户端、服务端程序为客户端服务端类
生活随笔
收集整理的這篇文章主要介紹了
封装利用libwebsockets写出的客户端、服务端程序为客户端服务端类
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
封裝利用libwebsockets寫出的客戶端、服務端程序為客戶端服務端類
文章目錄
- 封裝利用libwebsockets寫出的客戶端、服務端程序為客戶端服務端類
- 1.封裝
- 2.封裝后寫wss客戶端、服務端
- 3.測試結果
- 4.客戶端、服務端類程序
1.封裝
我們后續將使用c++來開發程序,因此有必要將用c寫成的wss客戶端、服務端程序作進一步封裝,使其成為wss客戶端類和服務端類,這樣更便于調用。封裝后的程序結構:
ubuntu@ubuntu-virtual-machine:~/work/test_libwebsockets/lws_class$ tree . ├── client │ ├── client │ ├── compile.sh │ ├── lws_client.cpp │ ├── lws_client.h │ └── test.cpp └── server├── compile.sh├── lws_server.cpp├── lws_server.h├── server└── test.cpp2 directories, 10 files2.封裝后寫wss客戶端、服務端
封裝后寫wss客戶端和服務端將非常方便,只需要幾行即可創建一個客戶端或者服務端:
服務端創建測試程序:
#include "lws_server.h" #include <signal.h>static int interrupted;void sigint_handler(int sig) {interrupted = 1; }int main(int argc,char **argv) {//接收SIGINT(ctrl+c)信號signal(SIGINT, sigint_handler);int n = 0;//創建服務器對象(可以指定端口,這里指定了8000)lws_server server(8000);//初始化服務器server.init();lwsl_notice("port:%d\n",server.get_port());//設置ssl(不使用ssl則傳空,使用則傳入證書文件路徑)server.set_ssl(NULL,NULL,NULL,0);//創建服務器server.create();//服務器運行(運行時可設置間隔等待時間,這里為1000,單位為ms)while(n >= 0 && !interrupted)n = server.run(1000);//銷毀資源server.destroy();return 0; }客戶端創建測試程序:
#include "lws_client.h" #include <signal.h>static int interrupted;void sigint_handler(int sig) {interrupted = 1; }int main(int argc,char **argv) {signal(SIGINT, sigint_handler);int n = 0;//創建客戶端對象(傳入服務器地址和端口)lws_client client("127.0.0.1",8000);//初始化客戶端client.init();//設置sslclient.set_ssl(NULL,NULL,NULL,0);//創建客戶端client.create();//連接服務器(需要ssl連接時傳入1,否則傳0)client.connect(0);//客戶端運行while(n >= 0 && !interrupted)n = client.run(1000);//銷毀資源client.destroy();return 0; }3.測試結果
運行服務端:
ubuntu@ubuntu-virtual-machine:~/work/test_libwebsockets/lws_class/server$ ./server [2018/09/12 18:34:56:3472] NOTICE: port:8000 [2018/09/12 18:34:56:3493] NOTICE: Creating Vhost 'default' port 8000, 1 protocols, IPv6 off運行客戶端:
ubuntu@ubuntu-virtual-machine:~/work/test_libwebsockets/lws_class/client$ ./client [2018/09/12 18:35:43:4919] NOTICE: Creating Vhost 'default' (serving disabled), 1 protocols, IPv6 off [2018/09/12 18:35:43:4947] NOTICE: Connected to server ok! [2018/09/12 18:35:43:4964] NOTICE: Tx: 你好 1 [2018/09/12 18:35:43:4987] NOTICE: Tx: 你好 2 [2018/09/12 18:35:43:4994] NOTICE: Rx: 你好 1 [2018/09/12 18:35:43:4999] NOTICE: Rx: 你好 2 [2018/09/12 18:35:43:5000] NOTICE: Tx: 你好 3 [2018/09/12 18:35:43:5007] NOTICE: Rx: 你好 3服務端:
ubuntu@ubuntu-virtual-machine:~/work/test_libwebsockets/lws_class/server$ ./server [2018/09/12 18:34:56:3472] NOTICE: port:8000 [2018/09/12 18:34:56:3493] NOTICE: Creating Vhost 'default' port 8000, 1 protocols, IPv6 off [2018/09/12 18:35:43:4939] NOTICE: Client connect! [2018/09/12 18:35:43:4979] NOTICE: recvied message:你好 1 [2018/09/12 18:35:43:4989] NOTICE: recvied message:你好 2 [2018/09/12 18:35:43:5001] NOTICE: recvied message:你好 3測試通信成功。目前服務器為回顯服務器,其底層采用的poll機制。
4.客戶端、服務端類程序
服務端程序類:
#include "lws_server.h"#define MAX_PAYLOAD_SIZE (10 * 1024)static struct lws_context_creation_info ctx_info = { 0 }; static struct lws_context *context = NULL;/*** 會話上下文對象,結構根據需要自定義*/ struct session_data {int msg_count;unsigned char buf[LWS_PRE + MAX_PAYLOAD_SIZE];int len;bool bin;bool fin; };/* 構造函數 */ lws_server::lws_server(int listen_port) {port = listen_port; }/* 析構函數 */ lws_server::~lws_server() {lwsl_notice("析構完成\n"); }/* 拷貝構造 */ lws_server::lws_server(const lws_server &obj) {}int lws_server::get_port() {return port; }/* 服務器底層實現的回調函數 */ static int protocol_my_callback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len ) {struct session_data *data = (struct session_data *) user;switch ( reason ) {case LWS_CALLBACK_ESTABLISHED: // 當服務器和客戶端完成握手后lwsl_notice("Client connect!\n");break;case LWS_CALLBACK_RECEIVE: // 當接收到客戶端發來的幀以后// 判斷是否最后一幀data->fin = lws_is_final_fragment( wsi );// 判斷是否二進制消息data->bin = lws_frame_is_binary( wsi );// 對服務器的接收端進行流量控制,如果來不及處理,可以控制之// 下面的調用禁止在此連接上接收數據lws_rx_flow_control( wsi, 0 );// 業務處理部分,為了實現Echo服務器,把客戶端數據保存起來memcpy( &data->buf[ LWS_PRE ], in, len );data->len = len;lwsl_notice("recvied message:%s\n",&data->buf[ LWS_PRE ]);// 需要給客戶端應答時,觸發一次寫回調lws_callback_on_writable( wsi );data = NULL;break;case LWS_CALLBACK_SERVER_WRITEABLE: // 當此連接可寫時lws_write( wsi, &data->buf[ LWS_PRE ], data->len, LWS_WRITE_TEXT );// 下面的調用允許在此連接上接收數據lws_rx_flow_control( wsi, 1 );break;}// 回調函數最終要返回0,否則無法創建服務器return 0; }/*** 支持的WebSocket子協議數組* 子協議即JavaScript客戶端WebSocket(url, protocols)第2參數數組的元素* 你需要為每種協議提供回調函數*/ struct lws_protocols protocols[] = {{//協議名稱,協議回調,接收緩沖區大小"ws", protocol_my_callback, sizeof( struct session_data ), MAX_PAYLOAD_SIZE,},{NULL, NULL, 0 // 最后一個元素固定為此格式} };void lws_server::init() {ctx_info.port = 8000;ctx_info.iface = NULL; // 在所有網絡接口上監聽ctx_info.protocols = protocols;ctx_info.gid = -1;ctx_info.uid = -1;ctx_info.options = LWS_SERVER_OPTION_VALIDATE_UTF8; }int lws_server::set_ssl(const char* ca_filepath, const char* server_cert_filepath,const char*server_private_key_filepath,bool is_support_ssl) {if(!is_support_ssl){ctx_info.ssl_ca_filepath = NULL;ctx_info.ssl_cert_filepath = NULL;ctx_info.ssl_private_key_filepath = NULL;}else{ctx_info.ssl_ca_filepath = ca_filepath;ctx_info.ssl_cert_filepath = server_cert_filepath;ctx_info.ssl_private_key_filepath = server_private_key_filepath;ctx_info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;//ctx_info.options |= LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT;}return is_support_ssl; }int lws_server::create() {context = lws_create_context(&ctx_info);if (!context) {lwsl_err("lws_server create failed\n");return -1;}return 1; }int lws_server::run(int wait_time) {return lws_service(context, wait_time); }void lws_server::destroy() {lws_context_destroy(context); } /******************************************************************************版權所有 (C), 2017-2019, ZY******************************************************************************文 件 名 : lws_server.h版 本 號 : 初稿作 者 : ZY生成日期 : 2018年9月12日 星期三最近修改 :功能描述 : lws_server.cpp 的頭文件函數列表 :修改歷史 :1.日 期 : 2018年9月12日 星期三作 者 : ZY修改內容 : 創建文件******************************************************************************//*----------------------------------------------** 包含頭文件 **----------------------------------------------*//*----------------------------------------------** 外部變量說明 **----------------------------------------------*//*----------------------------------------------** 外部函數原型說明 **----------------------------------------------*//*----------------------------------------------** 內部函數原型說明 **----------------------------------------------*//*----------------------------------------------** 全局變量 **----------------------------------------------*//*----------------------------------------------** 模塊級變量 **----------------------------------------------*//*----------------------------------------------** 常量定義 **----------------------------------------------*//*----------------------------------------------** 宏定義 **----------------------------------------------*/#ifndef __LWS_SERVER_H__ #define __LWS_SERVER_H__#ifdef __cplusplus #if __cplusplus extern "C"{ #endif #endif /* __cplusplus */#include <libwebsockets.h>class lws_server {private:int port;public:lws_server(int listen_port);~lws_server();lws_server(const lws_server &obj);int get_port();void init();int set_ssl(const char* ca_filepath, const char* server_cert_filepath,const char*server_private_key_filepath,bool is_support_ssl);int create();int run(int wait_time);void destroy(); };#ifdef __cplusplus #if __cplusplus } #endif #endif /* __cplusplus */#endif /* __LWS_SERVER_H__ */客戶端程序類:
#include "lws_client.h"#define MAX_PAYLOAD_SIZE 10 * 1024static struct lws_context_creation_info ctx_info = { 0 }; static struct lws_context *context = NULL; static struct lws_client_connect_info conn_info = { 0 }; static struct lws *wsi = NULL;/*** 會話上下文對象,結構根據需要自定義*/ struct session_data {int msg_count;unsigned char buf[LWS_PRE + MAX_PAYLOAD_SIZE];int len; };/*** 某個協議下的連接發生事件時,執行的回調函數** wsi:指向WebSocket實例的指針* reason:導致回調的事件* user 庫為每個WebSocket會話分配的內存空間* in 某些事件使用此參數,作為傳入數據的指針* len 某些事件使用此參數,說明傳入數據的長度*/ int lws_client_callback( struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len ) {struct session_data *data = (struct session_data *) user;switch ( reason ) {case LWS_CALLBACK_CLIENT_ESTABLISHED: // 連接到服務器后的回調lwsl_notice( "Connected to server ok!\n" );break;case LWS_CALLBACK_CLIENT_RECEIVE: // 接收到服務器數據后的回調,數據為in,其長度為lenlwsl_notice( "Rx: %s\n", (char *) in );break;case LWS_CALLBACK_CLIENT_WRITEABLE: // 當此客戶端可以發送數據時的回調if ( data->msg_count < 3 ) {// 前面LWS_PRE個字節必須留給LWSmemset( data->buf, 0, sizeof( data->buf ));char *msg = (char *) &data->buf[ LWS_PRE ];data->len = sprintf( msg, "你好 %d", ++data->msg_count );lwsl_notice( "Tx: %s\n", msg );// 通過WebSocket發送文本消息lws_write( wsi, &data->buf[ LWS_PRE ], data->len, LWS_WRITE_TEXT );}break;default:break; // lwsl_notice("not support\n");}return 0; }/*** 支持的WebSocket子協議數組* 子協議即JavaScript客戶端WebSocket(url, protocols)第2參數數組的元素* 你需要為每種協議提供回調函數*/ struct lws_protocols protocols[] = {{//協議名稱,協議回調,接收緩沖區大小"ws", lws_client_callback, sizeof( struct session_data ), MAX_PAYLOAD_SIZE,},{NULL, NULL, 0 // 最后一個元素固定為此格式} };/* 構造函數 */ lws_client::lws_client(char *_address,int _port) {server_address = _address;port = _port; }/* 析構函數 */ lws_client::~lws_client() {lwsl_notice("析構完成\n"); }/* 拷貝構造 */ lws_client::lws_client(const lws_client &obj) {}/* 初始化 */ void lws_client::init() {ctx_info.port = CONTEXT_PORT_NO_LISTEN;ctx_info.iface = NULL;ctx_info.protocols = protocols;ctx_info.gid = -1;ctx_info.uid = -1; }/* 設置ssl */ int lws_client::set_ssl(const char* ca_filepath, const char* server_cert_filepath,const char*server_private_key_filepath,bool is_support_ssl) {if(!is_support_ssl){ctx_info.ssl_ca_filepath = NULL;ctx_info.ssl_cert_filepath = NULL;ctx_info.ssl_private_key_filepath = NULL;}else{ctx_info.ssl_ca_filepath = ca_filepath;ctx_info.ssl_cert_filepath = server_cert_filepath;ctx_info.ssl_private_key_filepath = server_private_key_filepath;ctx_info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;//ctx_info.options |= LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT;}return is_support_ssl; }/* 創建客戶端 */ int lws_client::create() {// 創建一個WebSocket處理器context = lws_create_context( &ctx_info );if(!context)return -1;return 0; }/* 連接客戶端 */ int lws_client::connect(int is_ssl_support) {char addr_port[256] = { 0 };sprintf(addr_port, "%s:%u", server_address, port & 65535 );// 客戶端連接參數conn_info = { 0 };conn_info.context = context;conn_info.address = server_address;conn_info.port = port;if(!is_ssl_support)conn_info.ssl_connection = 0;elseconn_info.ssl_connection = 1;conn_info.path = "./";conn_info.host = addr_port;conn_info.origin = addr_port;conn_info.protocol = protocols[ 0 ].name;// 下面的調用觸發LWS_CALLBACK_PROTOCOL_INIT事件// 創建一個客戶端連接wsi = lws_client_connect_via_info( &conn_info );if(!wsi)return -1;return 1; }/* 運行客戶端 */ int lws_client::run(int wait_time) {lws_service( context, wait_time );/*** 下面的調用的意義是:當連接可以接受新數據時,觸發一次WRITEABLE事件回調* 當連接正在后臺發送數據時,它不能接受新的數據寫入請求,所有WRITEABLE事件回調不會執行*/lws_callback_on_writable( wsi ); }/* 銷毀 */ void lws_client::destroy() {lws_context_destroy(context); } /******************************************************************************版權所有 (C), 2017-2019, ZY******************************************************************************文 件 名 : lws_client.h版 本 號 : 初稿作 者 : ZY生成日期 : 2018年9月12日 星期三最近修改 :功能描述 : lws_client.cpp 的頭文件函數列表 :修改歷史 :1.日 期 : 2018年9月12日 星期三作 者 : ZY修改內容 : 創建文件******************************************************************************//*----------------------------------------------** 包含頭文件 **----------------------------------------------*//*----------------------------------------------** 外部變量說明 **----------------------------------------------*//*----------------------------------------------** 外部函數原型說明 **----------------------------------------------*//*----------------------------------------------** 內部函數原型說明 **----------------------------------------------*//*----------------------------------------------** 全局變量 **----------------------------------------------*//*----------------------------------------------** 模塊級變量 **----------------------------------------------*//*----------------------------------------------** 常量定義 **----------------------------------------------*//*----------------------------------------------** 宏定義 **----------------------------------------------*/#ifndef __LWS_CLIENT_H__ #define __LWS_CLIENT_H__#ifdef __cplusplus #if __cplusplus extern "C"{ #endif #endif /* __cplusplus */#include "libwebsockets.h"class lws_client {private:char *server_address;int port;public:lws_client(char *lws_server_address,int port);~lws_client();lws_client(const lws_client &obj);void init();int set_ssl(const char* ca_filepath, const char* server_cert_filepath,const char*server_private_key_filepath,bool is_support_ssl);int create();int connect(int is_ssl_support);int run(int wait_time);void destroy(); };#ifdef __cplusplus #if __cplusplus } #endif #endif /* __cplusplus */#endif /* __LWS_CLIENT_H__ */總結
以上是生活随笔為你收集整理的封装利用libwebsockets写出的客户端、服务端程序为客户端服务端类的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: qemu内存迁移格式
- 下一篇: 【Coding】LeetCode刷题记录