交叉编译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
相关产品推荐
相关产品推荐

