You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

交叉编译ARM V7后libwebsockets客户端出现连接异常断开问题

libwebsockets客户端在ARM平台发送首条消息后连接断开问题

我用libwebsockets开发了一个简易WebSocket客户端,在Ubuntu x86平台用gcc编译后运行正常,但用交叉编译工具链arm-rockchip830-linux-uclibcgnueabihf-gcc编译后部署到瑞芯微RV1106(ARM V7)板卡时,客户端发送第一条消息后连接立即断开。我在Ubuntu上用qemu-arm-static运行该ARM程序,结果和板卡上一致。

客户端代码如下:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libwebsockets.h>
#include <unistd.h>

static struct lws *wsi = NULL;
static int established = 0;
static int isBreak = 0;
static int stop = 0;

int UnmarshalURL(const char *_url, char *_protocol, char *_host, int *_port, char *path)
{
    char url[256];
    strncpy(url, _url, sizeof(url) - 1);
    url[sizeof(url) - 1] = '\0';
    
    char *pslash = strstr(url, "//");
    if (pslash)
    {
        strncpy(_protocol, url, pslash - url);
        _protocol[pslash - url] = '\0';
        strcpy(url, pslash + 2);
    }

    pslash = strchr(url, ':');
    if (!pslash)
    {
        *_port = -1;
        pslash = strchr(url, '/');
        if (!pslash)
        {
            strcpy(_host, url);
        }
        else
        {
            strncpy(_host, url, pslash - url);
            _host[pslash - url] = '\0';
            strcpy(path, pslash);
        }
    }
    else
    {
        strncpy(_host, url, pslash - url);
        _host[pslash - url] = '\0';
        strcpy(url, pslash + 1);
        pslash = strchr(url, '/');
        if (!pslash)
        {
            *_port = atoi(url);
        }
        else
        {
            char portStr[16];
            strncpy(portStr, url, pslash - url);
            portStr[pslash - url] = '\0';
            *_port = atoi(portStr);
            strcpy(path, pslash);
        }
    }
    return 0;
}

static int IsfirstMsg = 1;
static int DeviceId = 1;
static char writeBuffer[128];

int ws_callback(struct lws *_wsi, enum lws_callback_reasons _reasons, void *_user, void *_in, size_t _len)
{
    char buffer[2560] = {0};
    int len = 0;

    switch (_reasons)
    {
    case LWS_CALLBACK_CLIENT_ESTABLISHED:
        printf("Connection established\n");
        lws_callback_on_writable(_wsi);
        break;
    case LWS_CALLBACK_CLIENT_CLOSED:
        isBreak = 1;
        printf("Connection closed\n");
        break;
    case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
        printf("Connection error\n");
        break;
    case LWS_CALLBACK_CLIENT_RECEIVE:
        memcpy(buffer, _in, _len);
        printf("Received from server: %s\n", buffer);
        if (strcmp(buffer, "openCamera") == 0)
        {
            printf("Opening camera...\n");
            strcpy(writeBuffer, "CameraOK");
            lws_callback_on_writable(_wsi);
        }
        usleep(1000 * 1000);
        break;
    case LWS_CALLBACK_CLIENT_WRITEABLE:
        if (stop)
            break;
        if (IsfirstMsg)
        {
            snprintf(writeBuffer, sizeof(writeBuffer), "id:%d", DeviceId);
            IsfirstMsg = 0;
        }
        printf("Writing data: %s\n", writeBuffer);
        len = lws_write(_wsi, (unsigned char *)writeBuffer, strlen(writeBuffer), LWS_WRITE_TEXT);
        printf("Write length: %d\n", len);
        break;
    default:
        break;
    }
    return 0;
}

void *heartbeat_thread_func(void *arg)
{
    while (1)
    {
        if (!IsfirstMsg)
        {
            strcpy(writeBuffer, "beat");
            lws_callback_on_writable(wsi);
        }
        sleep(30);
    }
    return NULL;
}

int main(int argc, char **argv)
{
    if (argc < 2 || argc > 3)
    {
        printf("Usage: %s <url> [device_id]\n", argv[0]);
        return -1;
    }

    char protocol[16] = {0}, host[128] = {0}, path[128] = {0};
    int port = 0;

    UnmarshalURL(argv[1], protocol, host, &port, path);

    if (argc == 3)
        DeviceId = atoi(argv[2]);
    else
        DeviceId = 1;

    int ssl = (strcmp(protocol, "wss") == 0) ? 1 : 0;

    struct lws_context_creation_info info = {0};
    struct lws_client_connect_info ci = {0};
    struct lws_context *context;

    struct lws_protocols protocols[] = {
        {"ws-client", ws_callback, 0, 5120},
        {NULL, NULL, 0, 0}
    };

    info.protocols = protocols;
    info.port = CONTEXT_PORT_NO_LISTEN;
    info.options = ssl ? LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT : 0;

    context = lws_create_context(&info);
    if (!context)
    {
        printf("Failed to create context\n");
        return -1;
    }

    ci.context = context;
    ci.address = host;
    ci.port = port;
    ci.path = path;
    ci.host = ci.address;
    ci.origin = ci.address;
    ci.ssl_connection = ssl ? (LCCSCF_USE_SSL | LCCSCF_ALLOW_SELFSIGNED) : 0;
    ci.protocol = protocols[0].name;
    ci.pwsi = &wsi;

    lws_client_connect_via_info(&ci);

    pthread_t thread;
    if (pthread_create(&thread, NULL, heartbeat_thread_func, NULL) != 0)
    {
        printf("Failed to create heartbeat thread\n");
        return -1;
    }

    while (!isBreak)
    {
        lws_service(context, 500);
    }

    lws_context_destroy(context);
    return 0;
}

可能的原因及解决方法

1. libwebsockets库交叉编译兼容性问题

  • 确保交叉编译libwebsockets时,针对ARM V7架构和uclibc环境配置正确,开启客户端相关编译选项LWS_ROLE_CLIENT=ON。
  • 检查依赖库适配性:如果使用wss,需链接适配uclibc的SSL库,避免glibc版本的库导致兼容性问题。
  • 重新编译时添加-march=armv7-a参数,适配RV1106的硬件架构。

2. 全局变量竞态问题

代码中writeBuffer为全局变量,主线程回调和心跳线程同时修改它,在ARM+uclibc环境下可能触发数据错乱,导致WebSocket协议错误:

// 添加互斥锁保护全局缓冲区
static pthread_mutex_t buf_mutex = PTHREAD_MUTEX_INITIALIZER;

// 回调中修改缓冲区时加锁
pthread_mutex_lock(&buf_mutex);
snprintf(writeBuffer, sizeof(writeBuffer), "id:%d", DeviceId);
pthread_mutex_unlock(&buf_mutex);

// 心跳线程中修改缓冲区时加锁
pthread_mutex_lock(&buf_mutex);
strcpy(writeBuffer, "beat");
pthread_mutex_unlock(&buf_mutex);

3. 写入返回值未检查

当前代码未处理lws_write的失败情况,若写入异常会导致后续逻辑混乱,添加返回值检查:

len = lws_write(_wsi, (unsigned char *)writeBuffer, strlen(writeBuffer), LWS_WRITE_TEXT);
if (len < 0) {
    printf("lws_write failed: %d\n", len);
    isBreak = 1;
    return -1;
}

4. 开启调试日志定位问题

创建libwebsockets上下文时添加日志选项,查看连接断开的具体原因:

info.options |= LWS_SERVER_OPTION_LOG_LEVEL_DEBUG;

5. uclibc字符串函数差异

uclibc的字符串函数行为与glibc存在细微差异,手动确保writeBuffer以\0结尾,或使用更安全的字符串操作函数。


内容的提问来源于stack exchange,提问作者William Clark

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 00:02:19