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

2023-05-16

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


文章目录

  • 利用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 * 1024

void 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:           // 当接收到客户端发来的帧以后
            // 判断是否最后一帧
            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;
            printf("recvied message:%s\n",in);
 
            // 需要给客户端应答时,触发一次写回调
            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,否则无法创建服务器
    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 // 最后一个元素固定为此格式
    }
};
 
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; // 在所有网络接口上监听
    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/bash

libdir=libwebsockets

g++ -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 * 1024
 
void 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;
};
 
/**
 * 某个协议下的连接发生事件时,执行的回调函数
 *
 * 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,其长度为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;
    }
    return 0;
}
 
/**
 * 支持的WebSocket子协议数组
 * 子协议即JavaScript客户端WebSocket(url, protocols)第2参数数组的元素
 * 你需要为每种协议提供回调函数
 */
struct lws_protocols protocols[] = {
    {
        //协议名称,协议回调,接收缓冲区大小
        "ws", callback, sizeof( struct session_data ), MAX_PAYLOAD_SIZE,
    },
    {
        NULL, NULL,   0 // 最后一个元素固定为此格式
    }
};
 
int main() {
    // 信号处理函数
    signal( SIGTERM, sighdl );
 
    // 用于创建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;
 
    // 创建一个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;
 
    // 下面的调用触发LWS_CALLBACK_PROTOCOL_INIT事件
    // 创建一个客户端连接
    struct lws *wsi = lws_client_connect_via_info( &conn_info );
    while ( !exit_sig ) {
        // 执行一次事件循环(Poll),最长等待1000毫秒
        lws_service( context, 1000 );
        /**
         * 下面的调用的意义是:当连接可以接受新数据时,触发一次WRITEABLE事件回调
         * 当连接正在后台发送数据时,它不能接受新的数据写入请求,所有WRITEABLE事件回调不会执行
         */
        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/bash

libdir=libwebsockets
 
g++ -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客户端分别代表发送和接收,都可以看一下):
客户端:

/*
 * 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); }
#endif

static 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配置参数需要改一下,是支持自签名证书的。

坑点

有个大家一直踩的坑点就是连接域名的时候一直报错,最后发现使用域名时是不用加wss://前缀的,大家注意一下,比如使用minimal-ws-client测试wss服务连接:

 ./lws-minimal-ws-client -s openhw.work.weixin.qq.com

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

利用libwebsockets写ws、wss服务端和客户端 的相关文章

  • 详细nginx配置websocket的wss协议

    需求 xff1a 小程序连接websocket xff0c 必须使用wss协议 nginx配置websocket以及wss协议 其实wss协议就是使用了ssl协议的域名所自带的协议 xff0c 所以wss协议需要域名来进行socket连接
  • 【SpringBoot+阿里云ssl】完成 http转https以及ws转wss

    SpringBoot 43 阿里云ssl完成 http转https以及ws转wss 前言一 HTTP转HTTPS二 ws转wss 前言 本文工作是建立在已经有一个SSL证书的基础之上 阿里云证书如何申请可以参考以下链接 xff1a 阿里云免
  • go 编写tcp和udp服务端和客户端

    TCP协议 TCP IP Transmission Control Protocol Internet Protocol 即传输控制协议 网间协议 xff0c 是一种面向连接 xff08 连接导向 xff09 的 可靠的 基于字节流的传输层
  • ActiveMQ配置wss

    最近把前端页面由原来的http升级为了https xff0c 发现之前ActiveMQ提供的ws不能强求了 xff0c https服务下要求升级到wss 全网搜索了下 xff0c 没有找到一个靠谱的文档 一 证书准备 使用wss连接服务必须
  • 利用libwebsockets写ws、wss服务端和客户端

    利用libwebsockets写ws wss服务端和客户端 文章目录 利用libwebsockets写ws wss服务端和客户端服务端 xff1a 客户端官网例子坑点 服务端 xff1a server c span class token
  • python连接wss走自己的代理

    我开了一个vpn 然后用py写wss连接 怎么才能让他这个连接走我系统代理呢 vpn 开9090端口 set https proxy socks5 127 0 0 1 9090 set http proxy socks5 127 0 0 1
  • RoR 5.0.0 ActionCable wss WebSocket 握手:意外响应代码:301

    您好 我正在尝试使用 ror 5 0 0 beta 使用 puma 提供简单的聊天服务 在生产模式下工作 在本地主机中没有问题 这是我的Nginx配置 upstream websocket server 127 0 0 1 28080 se
  • 我可以从脚本或命令行将文件复制到网上邻居吗? [关闭]

    Closed 这个问题是无关 目前不接受答案 在 Windows XP 中 是否可以通过命令行 批处理文件或者更好的 PowerShell 脚本将文件复制到网上邻居 让我走上这条研究道路的是尝试将文件从用户的计算机发布到 WSS 3 0 文
  • 如何确定 SharePoint 列表的磁盘大小?

    我有一个包含大约 5500 个项目的列表 我想了解磁盘上的大小 我有什么办法可以做到这一点吗 如有必要 我不介意在数据库中运行查询 导航http myapplication mySitecollection layouts storman
  • wss 龙卷风连接问题

    龙卷风服务器 ssl options certfile server crt keyfile server key application Application server tornado httpserver HTTPServer a
  • Websocket 不支持 SSL

    https www hi todd com websocket 我使用 MQTT 协议创建了一个演示 它在 HTTP 连接下运行良好 但是当我尝试将 HTTP 更改为 https 时 出现连接错误 我已在 mosquitto conf 文件
  • 通过内置 Web 服务将文件上传到 SharePoint

    通过 WSS 3 0 版本公开的内置 Web 服务将文件上传到 SharePoint 服务器上的文档库的最佳方法是什么 根据两个最初的答案 我们肯定需要使用 Web 服务层 因为我们将从远程客户端应用程序进行这些调用 WebDAV 方法适合
  • 调试 Sharepoint 计时器作业

    我正在创建我的第一个计时器作业并想要对其进行调试 我已通过一项功能安装了计时器作业 并将其添加到 Web 应用程序的 JobDefinitions 集合中 并添加了一个 SPMinuteSchedule 每 5 分钟运行一次 用于测试目的
  • 发生意外的错误

    我已经在本地计算机上创建了一个 asp net Web 应用程序 并将其部署到 sharepoint 为此 我参考以下内容 在 Windows SharePoint Services 3 0 layouts 文件夹中部署 ASP NET W
  • 使用 CONTAINS 的 SharePoint 列表 CAML 查询

    我尝试在 Web 部件中使用以下 CAML 查询来查询 SharePoint 列表 我已经在 U2U CAML 查询生成器和 Stramit CAML 查看器中测试了该查询 它工作正常 仅返回匹配的记录 但是当我在 Web 部件中使用它时
  • 在会议工作区中以编程方式从列表中获取所有议程项目

    我想从定期会议工作区的特定列表中获取所有项目 我尝试执行以下 CAML
  • 在 Windows Sharepoint Services 3.0 中仅编辑拥有的列表项

    有没有办法限制 WSS 3 0 中的 编辑项目 权限 仅允许用户编辑自己的文档或列表项目 我们需要用户能够仅编辑他创建的文档 列表项目 而不是其他人创建的项目 因此 本质上我们需要 EDIT 权限和 ADD 权限的子集 这在 Windows
  • 设置 CLion 构建和二进制目录

    我正在尝试在 CLion 的项目中构建 libwebsockets 在构建期间 libwebsockets 创建其他文件所需的头文件并将其放入 PROJECT BINARY DIR 中 CLion 在为项目创建的随机构建目录中构建所有内容
  • 如何在 Ubuntu 中安装 libwebsocket 库?

    我正在尝试在我的 ubuntu 中安装 libwebsocket 所以我下载了该项目https github com warmcat libwebsockets https github com warmcat libwebsockets解
  • SharePoint 2007:如何在事务中执行一系列操作?

    我很想知道如何在事务中的 SharePoint 上下文中执行一系列操作 例如 我希望能够执行以下操作 context BeginTransaction listItemA Update listItemB Update context Co

随机推荐

  • mybatis-generator-core插件无法下载解决办法

    mybatis generator core插件无法下载解决办法 前言一 解决办法二 插件可用 前言 maven mybatis generator core插件无法下载 一 解决办法 二 插件可用
  • idea 去绿色波浪线

    idea 去绿色波浪线 如图 如图
  • stop-writes-on-bgsave-error

    redis RDB持久化配置 RDB持久化 配置 stop writes on bgsave error 选项 如果 61 yes xff1a redis 会创建一个新的后台进程dump rdb 假设 xff1a 创建快照 xff08 硬盘
  • px4_simple_example和uorb机制

    px4 simple app PX4 Autopilot src exampes px4 simple app xff0c 这个程序是用c语言调用orb API和poll机制订阅和发布通讯数据 xff0c 但是这个例子并不是既有接收又有发送
  • 四旋翼无人机硬件基础

    四旋翼无人机硬件基础 材料分析无刷电机型号转速 电子调速器作用规格四轴专用电调 xff1f 电调编程 xff1f 螺旋桨机制 规格 飞行控制器原理作用四轴 字四轴十字 电池锂电池 xff1f 电池容量电池片数放电能力插头类型T插XT60JS
  • AndroidStudio NDK开发环境配置及示例

    AndroidStudio NDK开发环境配置及示例 版本说明 版本作者日期备注0 1loon2020 8 26初稿 目录 文章目录 AndroidStudio NDK开发环境配置及示例版本说明目录一 Android Studio NDK环
  • Onvif协议学习:10、获取音视频流

    Onvif协议学习 xff1a 10 获取音视频流 文章目录 Onvif协议学习 xff1a 10 获取音视频流1 原理简介2 编码流程3 VLC播放RTSP视频4 准备FFmpeg库5 示例代码 原文链接 xff1a https blog
  • (ROS)RLException: [display_mrobot_with_camera.launch] is neither a launch file等一系列错误解决办法

    前提 xff1a 运行 roslaunch mrobot description display mrobot chassis urdf launch 有误 错误1 xff1a rviz直接打不开 错误显示 xff1a RLExceptio
  • 9款好用的SSH客户端软件推荐

    9款好用的SSH客户端软件推荐 文章目录 9款好用的SSH客户端软件推荐MobaXtermXshellTermiusPuTTYSimpleRemoteZOC TerminalFinalshellJuiceSSHServerCat SSH客户
  • 几款数据库连接工具

    几款数据库连接工具 文章目录 几款数据库连接工具1 前言2 Navicat3 DBeaver4 DataGrip5 phpMyAdmin 1 前言 我目前使用sqlite3和mysql数据库比较多 xff0c 所以对数据库客户端的需求主要是
  • win10文件资源管理器右键卡死现象解决方案大全

    win10文件资源管理器右键卡死现象解决方案大全 文章目录 win10文件资源管理器右键卡死现象解决方案大全一 右键桌面卡死 xff0c 过了许久都未反应二 右键文件夹或文件卡死2 1 方案12 2 方案22 3 方案3 xff08 靠谱
  • CLion常用插件及c文件模板配置

    CLion常用插件及c文件模板配置 文章目录 CLion常用插件及c文件模板配置1 常用插件2 模板2 1 c源文件模板 xff1a xff08 使用文件模板 xff09 2 2 c头文件模板 xff1a xff08 使用文件模板 xff0
  • Windows下安装及卸载程序可用的添加和删除当前路径到环境变量的bat脚本以及如何和inno setup结合使用的实例

    文章目录 1 安装bat脚本 install bat 将当前路径添加到环境变量中 2 卸载bat脚本 uninstall bat 搜索当前路径并删除 3 inno setup添加安装和卸载时执行上述bat脚本4 安装到 C盘权限不足问题处理
  • 浏览器播放rtsp视频流:4、jsmpeg+go实现局域网下的rtsp视频流web端播放

    文章目录 1 前言2 资料准备3 兼容性及适用性说明4 jsmpeg架构5 基于以上架构的go方案可行性分析6 编译和结果展示 xff08 编译坑点 xff09 7 最后 1 前言 之前的rtsp转webrtc的方案存在如下缺陷 xff1a
  • TCP套接字编程实例(一)

    TCP套接字编程实例 xff08 一 xff09 TCP套接字编程第一部分我们先用单线程简单实现客户端的 发 和服务器的 收 话不多说 xff0c 直接上代码 1 Client部分 xff1a include include include
  • Vm虚拟机扩展Ubuntu系统磁盘空间

    Vm虚拟机扩展Ubuntu系统磁盘空间 前言 一般我们在安装虚拟机时都会选择默认的20G磁盘空间 xff0c 但是一旦需要搭建一两个交叉编译环境后 xff0c 20G的空间就无法满足了 xff0c 我就是出现了这样的情况 xff0c 所以也
  • Qt添加菜单栏和工具栏

    Qt添加菜单栏和工具栏 版本说明 版本作者时间备注0 1loon2018 10 24初稿 目录 文章目录 Qt添加菜单栏和工具栏版本说明目录一 需求与目的二 详细说明三 最后 一 需求与目的 一般常规的PC软件都会有主窗口 xff0c 主窗
  • Qt关于tabwidget的使用及注意事项

    Qt关于tabwidget的使用及注意事项 版本说明 版本作者日期备注0 1loon2018 11 12初稿 目录 文章目录 Qt关于tabwidget的使用及注意事项版本说明目录一 需求和目的二 了解QTabWIDget类及用法1 详细描
  • SLIC算法介绍

    SLIC xff08 simple linear iterativeclustering xff09 xff0c 即 简单线性迭代聚类 x1f49b 它是2010年提出的一种思想简单 实现方便的算法 xff0c 将彩色图像转化为CIELAB
  • 利用libwebsockets写ws、wss服务端和客户端

    利用libwebsockets写ws wss服务端和客户端 文章目录 利用libwebsockets写ws wss服务端和客户端服务端 xff1a 客户端官网例子坑点 服务端 xff1a server c span class token