树莓派linux交叉编译libwebsocket及使用示例

本文详细介绍了如何在Linux环境下对libwebsockets库进行交叉编译,包括修改cmake配置、编译libwebsockets源码以及创建和使用客户端的示例代码。文章还提供了相关资源的下载链接,帮助读者理解和实践libwebsockets的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


前言

本文主要介绍libwebsockets在linux下的交叉编译,及demo例程的简单使用。


一、libwebsockets是什么?

官方回答:Libweb套接字 (LWS)是一个灵活、轻量级的纯C库,可使用非阻塞事件循环以极小的内存占用轻松实现现代网络协议。自2010年以来,它一直在不断开发,并被全球数千万设备和数千名开发人员使用。

二、使用到的资源

  1. cmake源码:https://download.csdn.net/download/m0_46592311/87558003
  2. libwebsockets源码:https://download.csdn.net/download/m0_46592311/87557999
  3. websocket测试工具:https://download.csdn.net/download/m0_46592311/87558013

三、编译

1. cmake

需修改cmake/CMakeLists.txt,在其中添加set(CMAKE_USE_OPENSSL OFF),可以直接添加到文件首行, 这里具体位置应该没有要求。用于解决没有openssl的问题。

步骤:

./configure 

sudo make

sudo make install

2. libwebsockets

由于用不到SSL功能,所以将配置项libwebsockets-main/CMakeLists.txt 中的LWS_WITH_SSL注释掉。
在这里插入图片描述
步骤:

cd libwebsockets-main
mkdir build
sudo cmake ..
sudo make
sudo make install
sudo ldconfig

四、 使用示例

做客户端

static int interrupted;

/* one of these created for each message */

struct msg
{
	char payload[40960 * 2]; /* is malloc'd */
	size_t len;
};

#define MAX_PAYLOAD_SIZE 1024

struct lws_rx
{
	unsigned char databuf[MAX_PAYLOAD_SIZE];
	int len;
	char rxflag;   // 接收标志
	char txflag;   // 待发送标志
	char txedflag; // 已发送标志
	int idcount;
};

struct per_vhost_data__minimal
{
	struct lws_context *context;
	struct lws_vhost *vhost;
	const struct lws_protocols *protocol;
	pthread_t pthread_spam[2];

	lws_sorted_usec_list_t sul;

	struct lws_client_connect_info i;
	struct lws *client_wsi;

	int counter;
	char finished;
	char established;
};


struct per_vhost_data__minimal data_minimal;


struct msg amsg;		 // 消息发送结构体
struct lws_rx lwsrx_buf; // 消息接收结构体

int jc_respond = 0; //

// 等待websocket响应
int lws_response(void)
{
	if ((lwsrx_buf.txedflag == 0) && (lwsrx_buf.txflag == 0))
		return 1;

	int timeout = 10 * 1000 / 200; // 超时等待时间为10秒
	while (timeout--)
	{
		if (lwsrx_buf.rxflag == 1) // 收到了数据应答
		{
			// lws_rxcontrol(lwsrx_buf); //解析RX接收内容
			lwsrx_buf.txflag = 0;
			lwsrx_buf.txedflag = 0;
			lwsrx_buf.rxflag = 0;
			return 1;
		}
		usleep(200 * 1000); // 200ms间抽一次接收数据
	}
	printf("ws响应超时!-------------------------------------.\n");
	return 0;
}

static void *thread_spam(void *d)
{
	struct per_vhost_data__minimal *vhd =
		(struct per_vhost_data__minimal *)d;

	int len = 40960 * 2, index = 1, n, whoami = 0;
	int TimeCnt = 0; // 计数
	std::string str_DevMsg;

	do
	{
		// 		/* don't generate output if client not connected */
		if (!vhd->established)
		{
			lwsl_user("websocket链接还未建立!\n");
			goto wait;
		}

		lwsl_user("ws填充发送数据内容.\n");
		n = lws_snprintf((char *)amsg.payload + LWS_PRE, (unsigned int)len,
						 "发送第%d条数据\n", TimeCnt);
		amsg.len = (unsigned int)n;
		lwsl_user("ws数据长度 %d.\n", amsg.len);
		lwsrx_buf.txflag = 1;
		/*
		 * This will cause a LWS_CALLBACK_EVENT_WAIT_CANCELLED
		 * in the lws service thread context.
		 */
		lws_cancel_service(vhd->context);

		lws_response(); // 等待响应及数据解析

	wait:
		sleep(10);
		TimeCnt++;
	} while (!vhd->finished);

	lwsl_notice("thread_spam %d exiting\n", whoami);

	pthread_exit(NULL);

	return NULL;
}

/*
 * The retry and backoff policy we want to use for our client connections
 */

static const uint32_t backoff_ms[] = {1000, 2000, 3000, 4000, 5000};

static const lws_retry_bo_t retry = {
	.retry_ms_table = backoff_ms,
	.retry_ms_table_count = LWS_ARRAY_SIZE(backoff_ms),
	.conceal_count = LWS_ARRAY_SIZE(backoff_ms) + 10,

	.secs_since_valid_ping = 30,		/* force PINGs after secs idle */
	.secs_since_valid_hangup = 30 + 30, //;//35, /* hangup after secs idle */

	.jitter_percent = 20,
};

static void
sul_connect_attempt(struct lws_sorted_usec_list *sul)
{
	struct per_vhost_data__minimal *vhd =
		lws_container_of(sul, struct per_vhost_data__minimal, sul);

	vhd->i.context = vhd->context;
	vhd->i.port = 5437;				 
	vhd->i.address = "192.168.1.100"; 

	string path = "/ws";
	vhd->i.path = path.c_str();
	vhd->i.host = vhd->i.address;
	vhd->i.origin = vhd->i.address;
	vhd->i.ssl_connection = 0;
	vhd->i.iface = NULL;
	vhd->i.protocol = "lws-minimal-broker";
	vhd->i.pwsi = &vhd->client_wsi;
	vhd->i.retry_and_idle_policy = &retry;

	lwsl_user("%s: 连接Ws服务器\n", __func__);
	if (!lws_client_connect_via_info(&vhd->i))
		lws_sul_schedule(vhd->context, 0, &vhd->sul,
						 sul_connect_attempt, 10 * LWS_US_PER_SEC);
}

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 = &amsg;
	void *retval;
	int n, m, r = 0;

	switch (reason)
	{

		/* --- protocol lifecycle callbacks --- */

	case LWS_CALLBACK_PROTOCOL_INIT:
		lwsl_user("%s: LWS_CALLBACK_PROTOCOL_INIT\n", __func__);
		vhd = (struct per_vhost_data__minimal *)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);

		/* start the content-creating threads */
		lwsl_user("%s: start the content-creating threads\n", __func__);

		if (pthread_create(&vhd->pthread_spam[0], NULL, thread_spam, vhd))
		{
			lwsl_err("thread creation failed\n");
			r = 1;
			goto init_fail;
		}
		sul_connect_attempt(&vhd->sul);
		break;

	case LWS_CALLBACK_PROTOCOL_DESTROY:
		lwsl_err("CLIENT_CONNECTION_ERROR\n");
	init_fail:
		vhd->finished = 1;
		pthread_join(vhd->pthread_spam[0], &retval);
		lws_sul_cancel(&vhd->sul);
		return r;

	case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
		lwsl_err("CLIENT_CONNECTION_ERROR: %s\n",
				 in ? (char *)in : "(null)");
		vhd->client_wsi = NULL;
		lws_sul_schedule(vhd->context, 0, &vhd->sul,
						 sul_connect_attempt, LWS_US_PER_SEC);
		break;

		/* --- client callbacks --- */

	case LWS_CALLBACK_CLIENT_ESTABLISHED:
		lwsl_user("%s: established\n", __func__);
		vhd->established = 1;
		break;
	case LWS_CALLBACK_CLIENT_RECEIVE:
		lwsl_user("RX: %s\n", (const char *)in);
		if (len < MAX_PAYLOAD_SIZE)
		{
			memcpy(lwsrx_buf.databuf, in, len); // 拷贝数据
			lwsrx_buf.rxflag = 1;				// 收到数据标志
			lwsrx_buf.len = len;
		}
		break;

	case LWS_CALLBACK_CLIENT_WRITEABLE:
		lwsl_user("%s: LWS_CALLBACK_CLIENT_WRITEABLE\n", __func__);

		if (lwsrx_buf.txflag == 0) // 无数据待发送
		{
			lwsl_user("%s: 无数据需要发送.\n", __func__);
			break;
		}
		lwsrx_buf.txedflag = 1; // 已发送标志置位
		lwsrx_buf.txflag = 0;	// 防止重复发送

		printf("lws_write数据长度:%d\n", pmsg->len);
		printf("%s\n", (unsigned char *)pmsg->payload);
		/* 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)
		{
			lwsl_err("ERROR %d writing to ws socket\n", m);
			return -1;
		}

		break;

	case LWS_CALLBACK_CLIENT_CLOSED:
		lwsl_user("%s: LWS_CALLBACK_CLIENT_CLOSED\n", __func__);
		vhd->client_wsi = NULL;
		vhd->established = 0;
		lws_sul_schedule(vhd->context, 0, &vhd->sul,
						 sul_connect_attempt, LWS_US_PER_SEC);
		break;

	case LWS_CALLBACK_EVENT_WAIT_CANCELLED:
		lwsl_user("%s: LWS_CALLBACK_EVENT_WAIT_CANCELLED\n", __func__);
		/*
		 * 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;

	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, 0, NULL, 0},
	LWS_PROTOCOL_LIST_TERM};

// 启动websocket,断线重连
void *t_WebSocketOpenRun(void *para)
{
	printf("websocket初始化.\n");

	sleep(10); // 开机等10秒,等待4G拨号先,当然该程序不存在先后

	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 */;

	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;
	}

	while (n >= 0 && !interrupted)
	{
		n = lws_service(context, 0);
	}

	lws_context_destroy(context);
	lwsl_user("Completed\n");

	return;
}


测试

用网线连接电脑和树莓派
在这里插入图片描述
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

点灯失败

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值