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.cpp 2 directories, 10 files 2.封装后写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(); //设置ssl client.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 * 1024 static 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,其长度为len lwsl_notice( "Rx: %s\n", (char *) in ); break; case LWS_CALLBACK_CLIENT_WRITEABLE: // 当此客户端可以发送数据时的回调 if ( data->msg_count < 3 ) { // 前面LWS_PRE个字节必须留给LWS memset( 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; else conn_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
标签:info,int,lws,server,ssl,client,libwebsockets,服务端,客户端 From: https://www.cnblogs.com/kn-zheng/p/17757287.html