cmdclear 2024-03-01 10:05 采纳率: 50%
浏览 65
已结题

libcurl使用无法连接服务器问题

#功能描述
嵌入式板子上有应用程序,调用libcurl,有两个子线程分别负责不同功能。
线程1:负责将文件传输至远程FTP服务器
线程2:负责与另一台嵌入式设备上的HTTP服务器协议进行通信(此流程为了降低功耗服务器只有在固定时间上电,执行完就断电)
#问题现象
在运行3个月后线程2开始出现问题,curl_easy_perform() failed[7]: Couldn't connect to server 无法连接至HTTP服务器,线程1始终正常在运行。
HTTP服务器为了降低功耗只有在固定时间上电,HTTP交互完就会断电。用别的设备连接过是没有问题的。
问题出现在线程2,不知道该如何排查

两个线程都是使用的下面流程
curl_easy_setopt -》 curl_easy_perform -》curl_easy_cleanup

如果说是网络资源耗尽导致无法再次创建链接,那么FTP传输也应该受影响才对,就不知道该怎么排查了
目前确定与HTTP服务器交互时,HTTP是正常启动的,而且HTTP是局域网内部链接,FTP与HTTP处于不同网段
这是与FTP交互的部分

    CURL *curl = curl_easy_init();
    for(i = 0;i < dbCount;i++)
    {
        sprintf(dbName,"%s/FTP%d.db",FTP2DIR,dbNum[i]);
        printf("dbName %s\n",dbName);

        if(1 == ftp_upload(dbName,FTP2,&uploadCount,curl))
        {
            break;
        }
    }
    curl_easy_cleanup(curl);

//ftp_upload
static int32 ftp_upload(const char8 *dbName,uint8 ftpNum,uint32 *uploadCount,CURL *curl)
{
    const COM_HANDLER_T *pDispHandler = NULL;
    if(*uploadCount >= MAX_UPLOAD_COUNT_ONE_TURN || curl == NULL)
    {
        return 0;
    }

    FTPMSG msg;
    struct stat file_info;
    
    CURLcode res;
    curl_off_t fsize;
    char8 auth[100] = {0};
    char8 ftpfile[150] = {0};
    char8 errmsg[300] = {0};
    int32 ret = 0;
    FILE *hd_src;

    int32 rowNumber = 0;

    while(1)
    {
        ftpsql_request(ftpNum);
        rowNumber = get_uploadFileNumber(dbName,ftpNum);
        ftpsql_release(ftpNum);
        if(rowNumber == 0 || rowNumber < 0)
        {
            printf("file need upload is none in %s\n",dbName);
            return 0;
        }

        memset(auth,0,sizeof(auth));
        memset(ftpfile,0,sizeof(ftpfile));
        memset(errmsg,0,sizeof(errmsg));
        ftpsql_request(ftpNum);
        get_uploadMessage(dbName,&msg,ftpNum);
        ftpsql_release(ftpNum);
        if(stat(msg.fileName, &file_info) < 0)   // /mnt/mmc1 目录下未找到
        {
            sprintf(errmsg,"ftpupload: Couldn't open %s: %s",msg.fileName, strerror(errno));
            log_WriteCurrentMessage(errmsg);
            printf("%s",errmsg);
            ftpsql_request(ftpNum);
            set_uploadMessage(dbName,&msg,UPLOAD_NOTEXIST,ftpNum);
            ftpsql_release(ftpNum);
            continue;
        }
        fsize = (curl_off_t)file_info.st_size;
        hd_src = fopen(msg.fileName, "rb");
        sprintf(auth,"%s:%s",msg.ftpUser,msg.ftpPasswd);
        ret = compose_ftpfilename(ftpfile,msg.fileName,msg.ftpDir,msg.ftpDirMode,msg.ftpIP,msg.ftpPort);
        if(ret != 0)
        {
            memset(errmsg,0,sizeof(errmsg));
            sprintf(errmsg,"compose_ftpfilename %s failed,ret %d",msg.fileName,ret);
            log_WriteCurrentMessage(errmsg);

            ret = compose_ftpfilename2(ftpfile,msg.fileName,msg.ftpDir,msg.ftpIP,msg.ftpPort);
            if(ret != 0)
            {
                ftpsql_request(ftpNum);
                set_uploadMessage(dbName,&msg,UPLOAD_FAIL,ftpNum);
                ftpsql_release(ftpNum);
            }
            continue;
        }
        memset(errmsg,0,sizeof(errmsg));
        sprintf(errmsg,"ftpfile %s",ftpfile);
        log_WriteCurrentMessage(errmsg);


        curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
        curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
        curl_easy_setopt(curl, CURLOPT_URL, ftpfile);
        curl_easy_setopt(curl, CURLOPT_READDATA, hd_src);
        curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE,(curl_off_t)fsize);
        curl_easy_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS, 1);    //目录不存在时,上传文件时,先创建目录
        //curl_easy_setopt(curl, CURLOPT_INTERFACE, port);
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, 120);
        curl_easy_setopt(curl, CURLOPT_USERPWD,auth); //user:passwd
        curl_easy_setopt(curl, CURLOPT_UPLOAD_BUFFERSIZE, 204800L);
        curl_easy_setopt(curl, CURLOPT_VERBOSE,1L);
        
        res = curl_easy_perform(curl);
        printf("\n\n\n");
        fclose(hd_src);
        if(res != CURLE_OK)
        {
            sprintf(errmsg,"FTP%d,ftpupload %s failed,[%d]%s",ftpNum,msg.fileName,res,curl_easy_strerror(res));
            log_WriteCurrentMessage(errmsg);
            strcat(errmsg,"\r\n");
            pDispHandler = drv_GetAutoDisplayHandler("CAPTURE");
            if (pDispHandler != NULL)
            {
                pDispHandler->sendBuffer(pDispHandler->handleID, errmsg, strlen(errmsg));
            }
            
            printf("%s\n",errmsg);
            ftpsql_request(ftpNum);
            set_uploadMessage(dbName,&msg,UPLOAD_FAIL,ftpNum);
            ftpsql_release(ftpNum);
            curl_easy_reset(curl);
            update_uploadFailedCounts();
            return 1;
        }
        else
        {
            sprintf(errmsg,"FTP%d,ftpupload %s success",ftpNum,msg.fileName);
            log_WriteCurrentMessage(errmsg);
            pDispHandler = drv_GetAutoDisplayHandler("CAPTURE");
            strcat(errmsg,"\r\n");
            if (pDispHandler != NULL)
            {
                pDispHandler->sendBuffer(pDispHandler->handleID, errmsg, strlen(errmsg));
            }
            ftpsql_request(ftpNum);
            set_uploadMessage(dbName,&msg,UPLOAD_SUCCESS,ftpNum);
            ftpsql_release(ftpNum);
            *uploadCount = *uploadCount + 1;
            curl_easy_reset(curl);
            update_uploadFailedCounts_cleanUp();
        }
        if(*uploadCount >= MAX_UPLOAD_COUNT_ONE_TURN)
        {
            break;
        }
    }
    return 0;
}

这是与HTTP交互的部分

int get_IdentificationModuleState(MemoryStruct chunk,char *jsonMsg,char *recbuf)
{
    char logmsg[200] = {0};
    char url[200] = {0};
    uint8   ip[4];
    drv_GetRemoteHttpServerIP(ip);
    uint16 port = drv_GetRemoteHttpServerPort();

    CURLcode res;
    CURL *curl = curl_easy_init();
    //memset(chunk.memory,0,sizeof(chunk.memory));
    chunk.size = 0;
    struct curl_slist *headers = NULL;
    headers = curl_slist_append(headers, "Content-Type:application/json");
    headers = curl_slist_append(headers, "charset=UTF-8");
    headers = curl_slist_append(headers, "Expect:");

    if(curl)
    {
        sprintf(url,"http://%u.%u.%u.%u:%d/v1/state",ip[0],ip[1],ip[2],ip[3],port);
        //curl_easy_setopt(curl, CURLOPT_URL, "http://192.168.14.147:80/v1/state");
        curl_easy_setopt(curl, CURLOPT_URL, url);

        //设置请求头信息
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
        //不接收头信息
        curl_easy_setopt(curl, CURLOPT_HEADER, 0);
        //尝试连接等待时间 5s
        curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5L);
        //执行最大等待时间 10s
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);

        //curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
        //curl_easy_setopt(curl, CURLOPT_TCP_KEEPIDLE, 10L);
        //curl_easy_setopt(curl, CURLOPT_TCP_KEEPINTVL, 5L);
        curl_easy_setopt(curl, CURLOPT_HTTPPOST, 1);

        //设置POST的JSON数据
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonMsg);
        curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)strlen(jsonMsg));
        
        //设置接受处理
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, http_post_callback);
            
        //禁用链接池功能
        //curl_easy_setopt(curl, CURLOPT_FRESH_CONNECT, 1l);

        /* get verbose debug output please */
        if(1)
        {
            curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
            //curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, 1L);
        }
        /* Perform the request, res will get the return code */
        res = curl_easy_perform(curl);
        /* Check for errors */
        if(res != CURLE_OK)
        {
            sprintf(logmsg,"get_IdentificationModuleState,curl_easy_perform() failed[%d]: %s",res,curl_easy_strerror(res));
            printf("%s\n",logmsg);
            log_WriteCurrentMessage(logmsg);
        }
        /* always cleanup */
        curl_slist_free_all(headers);
        curl_easy_cleanup(curl);
    }
    else
    {
        return -1;
    }

    if(chunk.memory != NULL)
    {
        //memset(recbuf,0,sizeof(recbuf));
        strncpy(recbuf,chunk.memory,chunk.size);
        printf("RECV:\n%s\n",recbuf);
    }
    return res;
}

  • 写回答

14条回答 默认 最新

  • 专家-皆知 2024-03-01 10:07
    关注

    引自免费微信小程序:皆我百晓生

    在您的问题中,线程2负责与HTTP服务器通信,该服务器有特殊的功耗管理策略。根据您描述的问题现象,curl_easy_perform()在运行3个月后开始失败并提示“Couldn't connect to server”,同时线程1(FTP传输)正常运行。鉴于此情况,以下是详细的排查步骤和建议:

    排查步骤:

    1. 网络连接监控

      • 检查线程2尝试连接HTTP服务器时的网络状态,确保网络通道在HTTP服务器上电期间是畅通的。
      • 可以通过日志记录每次调用curl_easy_perform前后的网络状况,包括IP地址、端口号、以及curl错误代码和详细信息。
    2. 资源检查

      • 尽管您提到如果是因为资源耗尽导致无法创建链接,理论上FTP传输也会受影响,但请确认是否有限制libcurl或操作系统的并发连接数等资源限制。
      • 检查嵌入式板子上的系统资源(如内存、文件句柄数量等),确保在长时间运行后没有达到瓶颈。
    3. 时间同步问题

      • 确认您的嵌入式设备与HTTP服务器的时间同步性,特别是在HTTP服务器仅在特定时间窗口内开启的情况下,确保请求发送的时间准确无误。
    4. libcurl配置检查

      • 检查用于线程2的libcurl设置,尤其是超时选项(curl_easy_setopt(CURLOPT_TIMEOUT)),确保其能够适应HTTP服务器的响应时间。
      • 检查重试策略和DNS缓存,可能需要清理或更新DNS缓存,或者增加重试次数。
    5. HTTP服务器日志分析

      • 如果可能的话,在HTTP服务器端查看相关日志,确定在出现问题时间段内服务器是否已成功启动并准备好接收请求。
    6. 代码示例审查

      CURL *curl;
      CURLcode res;
      
      curl_global_init(CURL_GLOBAL_DEFAULT);
      
      curl = curl_easy_init();
      if(curl) {
          curl_easy_setopt(curl, CURLOPT_URL, "http://your.server.address");
          // 设置合适的超时时间
          curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L); // 示例值,具体按需求调整
          // 其他必要的libcurl选项设置
      
          res = curl_easy_perform(curl);
          if(res != CURLE_OK) {
              fprintf(stderr, "curl_easy_perform() failed: %s\n",
                      curl_easy_strerror(res));
              // 这里可以添加更详细的错误处理和日志记录
          }
      
          curl_easy_cleanup(curl);
      }
      curl_global_cleanup();
      
      // 在多线程环境中,确保每个线程独立初始化和清理curl句柄
      
    7. 线程安全问题

      • 虽然两个线程使用了相同的libcurl流程,但请注意libcurl本身不是完全线程安全的。所有与特定会话相关的数据(如CURL指针)应在各自线程中独立创建和销毁,避免资源共享冲突。
    8. 硬件故障排查

      • 对于嵌入式环境,考虑是否有潜在的硬件故障,例如网络接口控制器(NIC)、电源模块或存储器等相关组件。
    9. 协议兼容性和版本问题

      • 确保使用的libcurl版本与HTTP服务器支持的协议版本兼容。

    总之,针对此类问题,请从多个角度进行排查,包括但不限于网络连接、资源管理、程序逻辑、服务器状态等方面,并结合实际应用环境进行细致调试。同时,优化代码以适应特定场景下的限制条件,比如为HTTP交互设定合理的时间窗口和重试机制。

    评论

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 3月1日
  • 修改了问题 3月1日
  • 创建了问题 3月1日

悬赏问题

  • ¥15 metadata提取的PDF元数据,如何转换为一个Excel
  • ¥15 关于arduino编程toCharArray()函数的使用
  • ¥100 vc++混合CEF采用CLR方式编译报错
  • ¥15 coze 的插件输入飞书多维表格 app_token 后一直显示错误,如何解决?
  • ¥15 vite+vue3+plyr播放本地public文件夹下视频无法加载
  • ¥15 c#逐行读取txt文本,但是每一行里面数据之间空格数量不同
  • ¥50 如何openEuler 22.03上安装配置drbd
  • ¥20 ING91680C BLE5.3 芯片怎么实现串口收发数据
  • ¥15 无线连接树莓派,无法执行update,如何解决?(相关搜索:软件下载)
  • ¥15 Windows11, backspace, enter, space键失灵