方法是libuv用多事件循环来驱动。说起来容易,做起来还是比下面的方法更容易:
上图是某位网友的方法代表子大部分网络资料。此方法对部署不友好,因为软件仓库提供的libwebsockets是不能用了。如何简化部署,利用好现有的软件仓库呢?
libwebsockets曾经是无缝支持libuv的。不过,随着版本号的增大,它对libuv的支持越来越差了。首先快速回顾下libwebsockets的版本历史:
lws_context_create_info.options
增加LWS_SERVER_OPTION_LIBUV
导致lws_service
函数崩溃。复现问题的常规操作:
编译时开启LIBUV支持:在编译libwebsockets库时,需要开启LIBUV支持并指定LIBUV的目录位置。这可以通过修改CMakeLists.txt
文件来实现。具体来说,需要在CMakeLists.txt
文件中添加以下行:
option(WITH_LIBUV "Enable libuv support" ON)
find_package(libuv REQUIRED)
include_directories(${LIBUV_INCLUDE_DIRS})
link_directories(${LIBUV_LIBRARY_DIRS})
add_definitions(-DLIBUV_SUPPORT=1)
这将启用LIBUV支持,并自动搜索和链接LIBUV库。
修改线程模型:libwebsockets默认使用自己的线程模型,但可以使用libuv的线程模型替代。这需要在初始化libwebsockets时指定使用libuv线程模型。具体来说,需要在调用lws_create_context
函数时,将thread_mode
参数设置为LWS_THREAD_MODE_UV
。例如:
struct lws_context_creation_info info;
memset(&info, 0, sizeof(info));
info.port = 8000;
info.protocols = protocols;
info.thread_mode = LWS_THREAD_MODE_UV; // 使用libuv线程模型
struct lws_context *context = lws_create_context(&info);
这将告诉libwebsockets使用libuv的线程模型进行初始化。
使用libuv的API:在使用libwebsockets时,需要使用libuv提供的API来进行读写操作和事件处理。例如,可以使用uv_read_start
和uv_write_t
等函数来进行读写操作,使用uv_run函数来进行事件循环。这些函数将在libuv库中提供。例如:
uv_stream_t *stream;
uv_buf_t buf;
uv_read_start(stream, on_read, &buf); // 开始读取操作
uv_write(&req, stream, buf.base, nread, on_write); // 开始写入操作
uv_run(uv_default_loop(), UV_RUN_DEFAULT); // 运行事件循环
每个线程一个循环。可以在同一线程中使用多个事件循环。但这通常没有意义,因为一个循环的 uv_run()
调用将阻止并停止另一个循环的运行。通过仔细组合 uv_run(loop, UV_RUN_ONCE)
你可以做一些非常有趣的事情。您可以使用多个循环在程序中创建“模态”步骤,其中第二个事件循环“暂停”第一个事件循环,直到发生某些操作(用户按 Return 键或您收到新事件或其他事件)。
有一个非常具体的用例,可以使用两个事件循环作为同步机制来代替条件变量。当时 libuv 没有条件变量支持,现在我保持这种方式,以允许它与早期的节点版本一起使用。具体用例是:
uv_queue_work()
在工作线程中调用阻塞函数。条件变量方法是:
uv_async_t
处理程序。此处理程序的回调调用自定义函数。uv_async_send()
来让主线程(事件循环运行的地方)代表它调用该函数。事件循环实现改为:
uv_async_t
与这个新循环关联起来。uv_async_t
处理程序的数据字段将此处理程序传递到主线程。uv_run()
新的事件循环,现在会阻塞,因为异步处理程序已经增加了它的 refcount
。uv_async_send()
向新循环上的异步处理程序发出信号。uv_run()
返回并且工作线程可以继续。下面的示例由TCP服务和websockets服务组成。TCP服务只是一个echo服务端,websockets则是静态文件服务,仅两个网页:index.html和404.html。
void tcp_thread_cb(void* args)
{
uv_loop_t loop;
uv_loop_init(&loop);
struct sockaddr_in addr;
uv_tcp_t server;
int ierr = uv_tcp_init(&loop, &server);
VOID_RETURN(ierr);
ierr = uv_ip4_addr("0.0.0.0", IPORT, &addr);
VOID_RETURN(ierr);
ierr = uv_tcp_bind(&server, (const struct sockaddr*)&addr, 0);
VOID_RETURN(ierr);
ierr = uv_listen((uv_stream_t*)&server, 128, on_new_connection);
VOID_RETURN(ierr);
printf("server started.\n");
ierr = uv_run(&loop, UV_RUN_DEFAULT);
VOID_RETURN(ierr);
uv_stop(&loop);
uv_barrier_wait((uv_barrier_t*)args);
}
void websockets_thread_cb(void* args)
{
static struct lws_context* context;
static const struct lws_http_mount mounts[] = { {
/* .mount_next */ NULL, /* linked-list "next" */
/* .mountpoint */ "/", /* mountpoint URL */
/* .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,
},{0,} };
struct lws_context_creation_info info = { 0, };
lws_set_log_level(LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE, NULL);
info.port = 20001;
info.mounts = mounts;
info.error_document_404 = "/404.html";
info.options = LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE;
context = lws_create_context(&info);
assert(context);
lwsl_user("websockets started\n");
while (1)
lws_service(context, 0);
lws_context_destroy(context);
uv_barrier_wait((uv_barrier_t*)args);
}
int main(int argc, char** argv)
{
uv_loop_t* loop = uv_default_loop();
uv_thread_t t[2];
uv_barrier_t b;
uv_barrier_init(&b, 3);
int ierr = uv_thread_create(&t[0], tcp_thread_cb, &b);
RAISE_RETURN(ierr);
ierr = uv_thread_create(&t[1], websockets_thread_cb, &b);
RAISE_RETURN(ierr);
ierr = uv_run(loop, UV_RUN_DEFAULT);
RAISE_RETURN(ierr);
uv_barrier_wait(&b);
return 0;
}
#include
#include
#include
#include
#include
#define RAISE_RETURN(x) \
if((x)) \
{ \
fprintf(stderr, "error {%s} code %ld %s\n", __func__, (long int)(x), uv_strerror(x)); \
printf("error {%s} code %ld %s\n", __func__, (long int)(x), uv_strerror(x)); \
raise(x); \
return (x); \
}
#define VOID_RETURN(x) \
if((x)) \
{ \
fprintf(stderr, "error {%s} code %ld %s\n", __func__, (long int)(x), uv_strerror(x)); \
printf("error {%s} code %ld %s\n", __func__, (long int)(x), uv_strerror(x)); \
return; \
}
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
作者: | 岬淢箫声 |
日期: | 2023年11月7日 |
版本: | 1.0 |
链接: | http://caowei.blog.csdn.net |