【问题标题】:libuv simple send udplibuv 简单发送 udp
【发布时间】:2020-11-19 20:57:02
【问题描述】:

我正在用 C 语言做一个多平台共享库,它使用 libuv 发送 UDP 消息,但是我对 libuv 了解不多,也不知道我的实现是否良好,或者除了 libuv 是否还有其他解决方案.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <uv.h>

#define IP "0.0.0.0"
#define PORT 8090
#define STR_BUFFER 256

void on_send(uv_udp_send_t *req, int status) {
    if (status) {
        fprintf(stderr, "Send error %s\n", uv_strerror(status));
        return;
    }
}

int send_udp(char *msg){
    uv_loop_t *loop = malloc(sizeof(uv_loop_t));
    uv_loop_init(loop);
    
    uv_udp_t send_socket;
    uv_udp_init(loop, &send_socket);
    
    struct sockaddr_in send_addr;
    uv_ip4_addr(IP, PORT, &send_addr);
    
    uv_udp_bind(&send_socket, (const struct sockaddr*)&send_addr, 0);

    char buff[STR_BUFFER];
    memset(buff,0,STR_BUFFER);
    strcpy(buff,msg);

    uv_buf_t buffer = uv_buf_init(buff,STR_BUFFER);
    
    uv_udp_send_t send_req;
    uv_udp_send(&send_req, &send_socket, &buffer, 1, (const struct sockaddr*)&send_addr, on_send);
    
    uv_run(loop, UV_RUN_ONCE);
    
    uv_loop_close(loop);
    free(loop);
    
    return 0;
}

int main() {
    send_udp("test 123\n");
    return 0;
}

【问题讨论】:

    标签: c udp libuv


    【解决方案1】:

    迄今为止,您的实施存在多个问题:

    1. 我不确定单个循环迭代是否足以在每个平台上发送 UDP 消息。这是您可以通过uv_run 返回的值轻松检查的内容,使用UV_RUN_ONCE 模式时,请参阅documentation for uv_run

    UV_RUN_ONCE: 轮询 i/o 一次。请注意,如果没有挂起的回调,此函数将阻塞。完成时返回零(没有剩余的活动句柄或请求),如果需要更多回调,则返回非零(意味着您应该在将来的某个时间再次运行事件循环)。

    如果您要保持代码原样,我建议您至少这样做:

    int done;
    do {
        done = uv_run(loop, UV_RUN_ONCE);
    } while (done != 0);
    

    但是继续阅读,你可以做得更好! :)

    1. 在性能方面成本相当高,uv_loops 应该是持久的,而不是为每条发送的消息创建。
    2. 不完整的错误处理:uv_udp_binduv_udp_send,...它们可能会失败!

    如何改进

    我建议您将代码更改为以下两种解决方案之一:

    • 您的库在 libuv 上下文中使用(即,您不会尝试隐藏 libuv 实现细节,而是要求所有希望使用您的库的人明确使用 libuv。
      然后,您可以将函数签名更改为 int send_udp(uv_loop_t *loop, char *msg) 之类的内容,并让库用户管理事件循环并运行它。

    • 您的库使用 libuv 作为实现细节:您不想用 libuv 打扰您的库用户,因此您有责任提供健壮和高性能的代码。我会这样做:

      • mylib_init:启动一个线程并在其上运行uv_loop
      • send_udp:将消息推送到队列中(注意线程安全),通知您的循环它有一条消息要发送(您可以为此使用uv_async),然后您可以使用与您大致相同的代码发送消息已经在使用了。
      • mylib_shutdown:停止循环和线程(同样,您可以使用uv_async 从右侧线程调用uv_stop

    看起来像这样(我没有要测试的编译器,但你会完成大部分工作):

    static uv_thread_t thread; // our network thread
    static uv_loop_t loop; // the loop running on the thread
    static uv_async_t notify_send; // to notify the thread it has messages to send
    static uv_async_t notify_shutdown; // to notify the thread it must shutdown
    static queue_t buffer_queue; // a queue of messages to send
    static uv_mutex_t buffer_queue_mutex; // to sync access to the queue from the various threads
    
    static void thread_entry(void *arg);
    static void on_send_messages(uv_async_t *handle);
    static void on_shutdown(uv_async_t *handle);
    
    int mylib_init() {
        // will call thread_entry on a new thread, our network thread
        return uv_thread_create(&thread, thread_entry, NULL);
    }
    
    int send_udp(char *msg) {
        uv_mutex_lock(&buffer_queue_mutex);
    
        queue_enqueue(&buffer_queue, strdup(msg)); // don't forget to free() after sending the message
        uv_async_send(&notify_send);
    
        uv_mutex_unlock(&buffer_queue_mutex);
    }
    
    int mylib_shutdown() {
        // will call on_shutdown on the loop thread
        uv_async_send(&notify_shutdown);
    
        // wait for the thread to stop
        return uv_thread_join(&thread);
    }
    
    static void thread_entry(void *arg) {
        uv_loop_init(&loop);
        uv_mutex_init_recursive(&buffer_queue_mutex);
        uv_async_init(&loop, &notify_send, on_send_messages);
        uv_async_init(&loop, &notify_shutdown, on_shutdown);
    
        uv_run(&loop, UV_RUN_DEFAULT); // this code will not return until uv_stop is called
    
        uv_mutex_destroy(&buffer_queue_mutex);
        uv_loop_close(&loop);
    }
    
    static void on_send_messages(uv_async_t *handle) {
        uv_mutex_lock(&buffer_queue_mutex);
    
        char *msg = NULL;
        // for each member of the queue ...
        while (queue_dequeue(&buffer_queue, &msg) == 0) {
            // create a uv_udp_t, send the message
        }
    
        uv_mutex_unlock(&buffer_queue_mutex);
    }
    
    static void on_shutdown(uv_async_t *handle) {
        uv_stop(&loop);
    }
    

    由您来开发或查找队列实现;)

    用法

    int main() {
        mylib_init();
    
        send_udp("my super message");
        
        mylib_shutdown();
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-03
      • 1970-01-01
      • 2012-11-09
      相关资源
      最近更新 更多