日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

利用libwebsockets写ws、wss服务端和客户端

發(fā)布時間:2023/12/20 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 利用libwebsockets写ws、wss服务端和客户端 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

服務端:

server.c

#include "libwebsockets.h" #include <signal.h> #include <string.h>static volatile int exit_sig = 0; #define MAX_PAYLOAD_SIZE 10 * 1024void sighdl( int sig ) {lwsl_notice( "%d traped", sig );exit_sig = 1; }/*** 會話上下文對象,結構根據需要自定義*/ struct session_data {int msg_count;unsigned char buf[LWS_PRE + MAX_PAYLOAD_SIZE];int len;bool bin;bool fin; };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: // 當服務器和客戶端完成握手后printf("Client connect!\n");break;case LWS_CALLBACK_RECEIVE: // 當接收到客戶端發(fā)來的幀以后// 判斷是否最后一幀data->fin = lws_is_final_fragment( wsi );// 判斷是否二進制消息data->bin = lws_frame_is_binary( wsi );// 對服務器的接收端進行流量控制,如果來不及處理,可以控制之// 下面的調用禁止在此連接上接收數據lws_rx_flow_control( wsi, 0 );// 業(yè)務處理部分,為了實現Echo服務器,把客戶端數據保存起來memcpy( &data->buf[ LWS_PRE ], in, len );data->len = len;printf("recvied message:%s\n",in);// 需要給客戶端應答時,觸發(fā)一次寫回調lws_callback_on_writable( wsi );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,否則無法創(chuàng)建服務器return 0; }/*** 支持的WebSocket子協議數組* 子協議即JavaScript客戶端WebSocket(url, protocols)第2參數數組的元素* 你需要為每種協議提供回調函數*/ struct lws_protocols protocols[] = {{//協議名稱,協議回調,接收緩沖區(qū)大小"ws", protocol_my_callback, sizeof( struct session_data ), MAX_PAYLOAD_SIZE,},{NULL, NULL, 0 // 最后一個元素固定為此格式} };int main(int argc,char **argv) {// 信號處理函數signal( SIGTERM, sighdl );struct lws_context_creation_info ctx_info = { 0 };ctx_info.port = 8000;ctx_info.iface = NULL; // 在所有網絡接口上監(jiān)聽ctx_info.protocols = protocols;ctx_info.gid = -1;ctx_info.uid = -1;ctx_info.options = LWS_SERVER_OPTION_VALIDATE_UTF8;ctx_info.ssl_ca_filepath = "../ca/ca-cert.pem";ctx_info.ssl_cert_filepath = "./server-cert.pem";ctx_info.ssl_private_key_filepath = "./server-key.pem";ctx_info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;//ctx_info.options |= LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT;struct lws_context *context = lws_create_context(&ctx_info);while ( !exit_sig ) {lws_service(context, 1000);}lws_context_destroy(context);return 0; }

編譯腳本,compile.sh

######################################################################### # File Name: compile.sh # Author: loon # mail: 2453419889@qq.com # Created Time: 2018年09月07日 星期五 10時08分52秒 ######################################################################### #!/bin/bashlibdir=libwebsocketsg++ -g -o server server.c -I$libdir/include -L$libdir/lib -lwebsockets

客戶端

client.c

#include "libwebsockets.h" #include <signal.h>static volatile int exit_sig = 0; #define MAX_PAYLOAD_SIZE 10 * 1024void sighdl( int sig ) {lwsl_notice( "%d traped", sig );exit_sig = 1; }/*** 會話上下文對象,結構根據需要自定義*/ struct session_data {int msg_count;unsigned char buf[LWS_PRE + MAX_PAYLOAD_SIZE];int len; };/*** 某個協議下的連接發(fā)生事件時,執(zhí)行的回調函數** wsi:指向WebSocket實例的指針* reason:導致回調的事件* user 庫為每個WebSocket會話分配的內存空間* in 某些事件使用此參數,作為傳入數據的指針* len 某些事件使用此參數,說明傳入數據的長度*/ int 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: // 當此客戶端可以發(fā)送數據時的回調if ( data->msg_count < 3 ) {// 前面LWS_PRE個字節(jié)必須留給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發(fā)送文本消息lws_write( wsi, &data->buf[ LWS_PRE ], data->len, LWS_WRITE_TEXT );}break;}return 0; }/*** 支持的WebSocket子協議數組* 子協議即JavaScript客戶端WebSocket(url, protocols)第2參數數組的元素* 你需要為每種協議提供回調函數*/ struct lws_protocols protocols[] = {{//協議名稱,協議回調,接收緩沖區(qū)大小"ws", callback, sizeof( struct session_data ), MAX_PAYLOAD_SIZE,},{NULL, NULL, 0 // 最后一個元素固定為此格式} };int main() {// 信號處理函數signal( SIGTERM, sighdl );// 用于創(chuàng)建vhost或者context的參數struct lws_context_creation_info ctx_info = { 0 };ctx_info.port = CONTEXT_PORT_NO_LISTEN;ctx_info.iface = NULL;ctx_info.protocols = protocols;ctx_info.gid = -1;ctx_info.uid = -1;//ssl支持(指定CA證書、客戶端證書及私鑰路徑,打開ssl支持)ctx_info.ssl_ca_filepath = "../ca/ca-cert.pem";ctx_info.ssl_cert_filepath = "./client-cert.pem";ctx_info.ssl_private_key_filepath = "./client-key.pem";ctx_info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;// 創(chuàng)建一個WebSocket處理器struct lws_context *context = lws_create_context( &ctx_info );char address[] = "127.0.0.1";int port = 8000;char addr_port[256] = { 0 };sprintf(addr_port, "%s:%u", address, port & 65535 );// 客戶端連接參數struct lws_client_connect_info conn_info = { 0 };conn_info.context = context;conn_info.address = address;conn_info.port = port;conn_info.ssl_connection = 1;conn_info.path = "./";conn_info.host = addr_port;conn_info.origin = addr_port;conn_info.protocol = protocols[ 0 ].name;// 下面的調用觸發(fā)LWS_CALLBACK_PROTOCOL_INIT事件// 創(chuàng)建一個客戶端連接struct lws *wsi = lws_client_connect_via_info( &conn_info );while ( !exit_sig ) {// 執(zhí)行一次事件循環(huán)(Poll),最長等待1000毫秒lws_service( context, 1000 );/*** 下面的調用的意義是:當連接可以接受新數據時,觸發(fā)一次WRITEABLE事件回調* 當連接正在后臺發(fā)送數據時,它不能接受新的數據寫入請求,所有WRITEABLE事件回調不會執(zhí)行*/lws_callback_on_writable( wsi );}// 銷毀上下文對象lws_context_destroy( context );return 0; }

編譯腳本:

######################################################################### # File Name: compile.sh # Author: loon # mail: 2453419889@qq.com # Created Time: 2018年09月07日 星期五 10時22分58秒 ######################################################################### #!/bin/bashlibdir=libwebsocketsg++ -g -o client client.c -I$libdir/include -L$libdir/build/lib -lwebsockets

官網例子

其實下載了官網源碼后minimal-examples下面有很多的例子,里面已經寫好了cmake的文件,看一下對應的README后直接可以在對應位置編譯,我這里復制minimal-ws-client和一個minimal-ws-server的例子,我基本只改了服務地址和端口,然后增加了打印(還有tx和rx客戶端分別代表發(fā)送和接收,都可以看一下):
客戶端:

/** lws-minimal-ws-client-tx** Written in 2010-2019 by Andy Green <andy@warmcat.com>** This file is made available under the Creative Commons CC0 1.0* Universal Public Domain Dedication.** This demonstrates a ws "publisher" to go with the minimal-ws-broker* example.** Two threads are spawned that produce messages to be sent to the broker,* via a local ringbuffer. Locking is provided to make ringbuffer access* threadsafe.** When a nailed-up client connection to the broker is established, the* ringbuffer is sent to the broker, which distributes the events to all* connected clients.*/#include <libwebsockets.h> #include <string.h> #include <signal.h> #include <pthread.h>static int interrupted;/* one of these created for each message */struct msg {void *payload; /* is malloc'd */size_t len; };struct per_vhost_data__minimal {struct lws_context *context;struct lws_vhost *vhost;const struct lws_protocols *protocol;pthread_t pthread_spam[2];pthread_mutex_t lock_ring; /* serialize access to the ring buffer */struct lws_ring *ring; /* ringbuffer holding unsent messages */uint32_t tail;struct lws_client_connect_info i;struct lws *client_wsi;int counter;char finished;char established; };#if defined(WIN32) static void usleep(unsigned long l) { Sleep(l / 1000); } #endifstatic void __minimal_destroy_message(void *_msg) {struct msg *msg = _msg;free(msg->payload);msg->payload = NULL;msg->len = 0; }static void * thread_spam(void *d) {struct per_vhost_data__minimal *vhd =(struct per_vhost_data__minimal *)d;struct msg amsg;int len = 128, index = 1, n;do {/* don't generate output if client not connected */if (!vhd->established)goto wait;pthread_mutex_lock(&vhd->lock_ring); /* --------- ring lock { *//* only create if space in ringbuffer */n = (int)lws_ring_get_count_free_elements(vhd->ring);if (!n) {lwsl_user("dropping!\n");goto wait_unlock;}amsg.payload = malloc(LWS_PRE + len);if (!amsg.payload) {lwsl_user("OOM: dropping\n");goto wait_unlock;}n = lws_snprintf((char *)amsg.payload + LWS_PRE, len,"tid: %p, msg: %d",(void *)pthread_self(), index++);amsg.len = n;n = lws_ring_insert(vhd->ring, &amsg, 1);if (n != 1) {__minimal_destroy_message(&amsg);lwsl_user("dropping!\n");} else/** This will cause a LWS_CALLBACK_EVENT_WAIT_CANCELLED* in the lws service thread context.*/lws_cancel_service(vhd->context);wait_unlock:pthread_mutex_unlock(&vhd->lock_ring); /* } ring lock ------- */wait:usleep(100000);} while (!vhd->finished);lwsl_notice("thread_spam %p exiting\n", (void *)pthread_self());pthread_exit(NULL);return NULL; }static int connect_client(struct per_vhost_data__minimal *vhd) {vhd->i.context = vhd->context;vhd->i.port = 7681;vhd->i.address = "localhost";vhd->i.path = "/publisher";vhd->i.host = vhd->i.address;vhd->i.origin = vhd->i.address;vhd->i.ssl_connection = 0;vhd->i.protocol = "lws-minimal-broker";vhd->i.pwsi = &vhd->client_wsi;return !lws_client_connect_via_info(&vhd->i); }static int callback_minimal_broker(struct lws *wsi, enum lws_callback_reasons reason,void *user, void *in, size_t len) {struct per_vhost_data__minimal *vhd =(struct per_vhost_data__minimal *)lws_protocol_vh_priv_get(lws_get_vhost(wsi),lws_get_protocol(wsi));const struct msg *pmsg;void *retval;int n, m, r = 0;switch (reason) {/* --- protocol lifecycle callbacks --- */case LWS_CALLBACK_PROTOCOL_INIT:vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),lws_get_protocol(wsi),sizeof(struct per_vhost_data__minimal));vhd->context = lws_get_context(wsi);vhd->protocol = lws_get_protocol(wsi);vhd->vhost = lws_get_vhost(wsi);vhd->ring = lws_ring_create(sizeof(struct msg), 8,__minimal_destroy_message);if (!vhd->ring)return 1;pthread_mutex_init(&vhd->lock_ring, NULL);/* start the content-creating threads */for (n = 0; n < (int)LWS_ARRAY_SIZE(vhd->pthread_spam); n++)if (pthread_create(&vhd->pthread_spam[n], NULL,thread_spam, vhd)) {lwsl_err("thread creation failed\n");r = 1;goto init_fail;}if (connect_client(vhd))lws_timed_callback_vh_protocol(vhd->vhost,vhd->protocol, LWS_CALLBACK_USER, 1);break;case LWS_CALLBACK_PROTOCOL_DESTROY: init_fail:vhd->finished = 1;for (n = 0; n < (int)LWS_ARRAY_SIZE(vhd->pthread_spam); n++)if (vhd->pthread_spam[n])pthread_join(vhd->pthread_spam[n], &retval);if (vhd->ring)lws_ring_destroy(vhd->ring);pthread_mutex_destroy(&vhd->lock_ring);return r;case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:lwsl_err("CLIENT_CONNECTION_ERROR: %s\n",in ? (char *)in : "(null)");vhd->client_wsi = NULL;lws_timed_callback_vh_protocol(vhd->vhost,vhd->protocol, LWS_CALLBACK_USER, 1);break;/* --- client callbacks --- */case LWS_CALLBACK_CLIENT_ESTABLISHED:lwsl_user("%s: established\n", __func__);vhd->established = 1;break;case LWS_CALLBACK_CLIENT_WRITEABLE:pthread_mutex_lock(&vhd->lock_ring); /* --------- ring lock { */pmsg = lws_ring_get_element(vhd->ring, &vhd->tail);if (!pmsg)goto skip;/* notice we allowed for LWS_PRE in the payload already */m = lws_write(wsi, ((unsigned char *)pmsg->payload) + LWS_PRE,pmsg->len, LWS_WRITE_TEXT);if (m < (int)pmsg->len) {pthread_mutex_unlock(&vhd->lock_ring); /* } ring lock */lwsl_err("ERROR %d writing to ws socket\n", m);return -1;}lws_ring_consume_single_tail(vhd->ring, &vhd->tail, 1);/* more to do for us? */if (lws_ring_get_element(vhd->ring, &vhd->tail))/* come back as soon as we can write more */lws_callback_on_writable(wsi);skip:pthread_mutex_unlock(&vhd->lock_ring); /* } ring lock ------- */break;case LWS_CALLBACK_CLIENT_CLOSED:vhd->client_wsi = NULL;vhd->established = 0;lws_timed_callback_vh_protocol(vhd->vhost, vhd->protocol,LWS_CALLBACK_USER, 1);break;case LWS_CALLBACK_EVENT_WAIT_CANCELLED:/** When the "spam" threads add a message to the ringbuffer,* they create this event in the lws service thread context* using lws_cancel_service().** We respond by scheduling a writable callback for the* connected client, if any.*/if (vhd && vhd->client_wsi && vhd->established)lws_callback_on_writable(vhd->client_wsi);break;/* rate-limited client connect retries */case LWS_CALLBACK_USER:printf("用戶回調...\n");lwsl_notice("%s: LWS_CALLBACK_USER\n", __func__);if (connect_client(vhd))lws_timed_callback_vh_protocol(vhd->vhost,vhd->protocol,LWS_CALLBACK_USER, 1);break;default:break;}return lws_callback_http_dummy(wsi, reason, user, in, len); }static const struct lws_protocols protocols[] = {{"lws-minimal-broker",callback_minimal_broker,0,0,},{ NULL, NULL, 0, 0 } };static void sigint_handler(int sig) {interrupted = 1; }int main(int argc, const char **argv) {struct lws_context_creation_info info;struct lws_context *context;const char *p;int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE/* for LLL_ verbosity above NOTICE to be built into lws,* lws must have been configured and built with* -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE *//* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER *//* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY *//* | LLL_DEBUG */;signal(SIGINT, sigint_handler);if ((p = lws_cmdline_option(argc, argv, "-d")))logs = atoi(p);lws_set_log_level(logs, NULL);lwsl_user("LWS minimal ws client tx\n");lwsl_user(" Run minimal-ws-broker and browse to that\n");memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */info.protocols = protocols;/** since we know this lws context is only ever going to be used with* one client wsis / fds / sockets at a time, let lws know it doesn't* have to use the default allocations for fd tables up to ulimit -n.* It will just allocate for 1 internal and 1 (+ 1 http2 nwsi) that we* will use.*/info.fd_limit_per_thread = 1 + 1 + 1;context = lws_create_context(&info);if (!context) {lwsl_err("lws init failed\n");return 1;}while (n >= 0 && !interrupted)n = lws_service(context, 0);lws_context_destroy(context);lwsl_user("Completed\n");return 0; }

服務端:

/** lws-minimal-ws-server** Written in 2010-2019 by Andy Green <andy@warmcat.com>** This file is made available under the Creative Commons CC0 1.0* Universal Public Domain Dedication.** This demonstrates the most minimal http server you can make with lws,* with an added websocket chat server.** To keep it simple, it serves stuff in the subdirectory "./mount-origin" of* the directory it was started in.* You can change that by changing mount.origin.*/#include <libwebsockets.h> #include <string.h> #include <signal.h>#define LWS_PLUGIN_STATIC #include "protocol_lws_minimal.c"static struct lws_protocols protocols[] = {{ "http", lws_callback_http_dummy, 0, 0 },LWS_PLUGIN_PROTOCOL_MINIMAL,{ NULL, NULL, 0, 0 } /* terminator */ };static const lws_retry_bo_t retry = {.secs_since_valid_ping = 3,.secs_since_valid_hangup = 10, };static int interrupted;static const struct lws_http_mount mount = {/* .mount_next */ NULL, /* linked-list "next" *//* .mountpoint */ "/", /* mountpoint URL *//* .origin */ "./mount-origin", /* serve from dir *//* .def */ "index.html", /* default filename *//* .protocol */ NULL,/* .cgienv */ NULL,/* .extra_mimetypes */ NULL,/* .interpret */ NULL,/* .cgi_timeout */ 0,/* .cache_max_age */ 0,/* .auth_mask */ 0,/* .cache_reusable */ 0,/* .cache_revalidate */ 0,/* .cache_intermediaries */ 0,/* .origin_protocol */ LWSMPRO_FILE, /* files in a dir *//* .mountpoint_len */ 1, /* char count *//* .basic_auth_login_file */ NULL, };void sigint_handler(int sig) {interrupted = 1; }int main(int argc, const char **argv) {struct lws_context_creation_info info;struct lws_context *context;const char *p;int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE/* for LLL_ verbosity above NOTICE to be built into lws,* lws must have been configured and built with* -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE *//* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER *//* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY *//* | LLL_DEBUG */;signal(SIGINT, sigint_handler);if ((p = lws_cmdline_option(argc, argv, "-d")))logs = atoi(p);lws_set_log_level(logs, NULL);lwsl_user("LWS minimal ws server | visit http://localhost:7681 (-s = use TLS / https)\n");memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */info.port = 7681;info.mounts = &mount;info.protocols = protocols;info.vhost_name = "localhost";info.ws_ping_pong_interval = 10;info.options =LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE;if (lws_cmdline_option(argc, argv, "-s")) {lwsl_user("Server using TLS\n");info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;info.ssl_cert_filepath = "localhost-100y.cert";info.ssl_private_key_filepath = "localhost-100y.key";}if (lws_cmdline_option(argc, argv, "-h"))info.options |= LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK;if (lws_cmdline_option(argc, argv, "-v"))info.retry_and_idle_policy = &retry;context = lws_create_context(&info);if (!context) {lwsl_err("lws init failed\n");return 1;}while (n >= 0 && !interrupted)n = lws_service(context, 0);lws_context_destroy(context);return 0; }

由于服務端和客戶端支持的回調協議名不一樣,所以服務器這里一直打印,不影響使用:


下面有很多人問lws_getaddrinfo46 failed的問題,我懷疑是dns解析的問題,建議先使用ip和端口的形式試下,域名的問題我再看看源碼,暫時沒分析出原因,客戶端似乎沒辦法自動根據域名解析出對應的ip和端口,目前從源碼看是“ipv6 lws_getaddrinfo46 failed”。

還有客戶端一般可以用官網給的不加密客戶端,需要加密的話則需要證書,對于自簽名的證書options配置參數需要改一下,是支持自簽名證書的。

?

?

總結

以上是生活随笔為你收集整理的利用libwebsockets写ws、wss服务端和客户端的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。