今天在在写一个向一个url申请相关数据时发现了一个很奇怪的现象,就是有时候解析相应的数据时有时候报错,还不是必现的。。。(头痛),,然后通过增加打印,终于锁定了是因为发送后调用CURLOPT_WRITEFUNCTION接收的数据有时候不全,打印如下
那么问题又来了,为什么有时候没有接收全,,,,
于是我还是觉得自己代码写的不对,部分代码如下
static int getConfigData(TGAT1400ClientConfig *_pstTGAT1400ClientConfig,char *_sign,char *_nonce)
{
int iRet = -1;
CURL* curl;
CURLcode res;
char response[4096];
memset(response,0,sizeof(response));
char configurl[256];
memset(configurl,0,sizeof(configurl));
if(_sign == NULL ||_pstTGAT1400ClientConfig == NULL || _nonce == NULL )
{
Gat1400Log_Error("json_data _pstTGAT1400ClientConfig == NULL \n");
iRet = -1;
goto EXIT;
}
struct MemoryStruct chunk;
chunk.memory = malloc(1024); /* will be grown as needed by the realloc above */\
if (chunk.memory == NULL)
{
Gat1400Log_Error("chunk.memory == NULL\n");
iRet = -1;
goto EXIT;
}
chunk.size = 0; /* no data at this point */
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if (!curl)
{
Gat1400Log_Error("Failed to initialize CURL.\n");
curl_global_cleanup();
iRet = -1;
goto EXIT;
}
snprintf(configurl, 255,"https://aaabbb:8800", _pstTGAT1400ClientConfig->m_pcUserName, _nonce, _sign);//地址就不给了哈哈哈哈
Gat1400Log_Debug("configurl is [%s]\n",configurl);
response[0] = '\0';
curl_easy_setopt(curl, CURLOPT_URL, configurl);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 20L);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
if (strncmp(configurl, "https://", strlen("https://")) == 0)
{
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
}
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
Gat1400Log_Error("curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);
curl_global_cleanup();
iRet = -1;
goto EXIT;
}
// 解析返回的JSON数据
Gat1400Log_Error("chunk.memory is %s\n",chunk.memory);
iRet = parseJson(chunk.memory, _pstTGAT1400ClientConfig);
free(chunk.memory);
curl_easy_cleanup(curl);
curl_global_cleanup();
EXIT:
return iRet;
}
在使用parseJson解析的时候有时候解析不对,所以应该问题就出在WriteMemoryCallback回调里
WriteMemoryCallback接口如下
size_t WriteMemoryCallback(void *pvSrc, size_t size, size_t nmemb, void *pvStream)
{
if (NULL == pvSrc || NULL == pvStream)
{
Gat1400Log_Error("invalid pvSrc(%p) pvStream(%p)", pvSrc, pvStream);
return 0;
}
char *p = (char *)pvStream;
char *q = (char *)pvSrc;
int len = strlen(p);
(void)strncat(p, q, (1024 - len) - 1);
Gat1400Log_Debug("recv is %s\n", p);
//Gat1400Log_Debug("nmemb * size=%d", nmemb * size);
return nmemb * size;
}
emmm当时这个接口直接调用部门公共库的,感觉也不会太大的问题!仔细分析一下
在使用strncat
函数拼接数据时,使用了一个大小为1024的缓冲区 p
,但是在计算当前数据长度 len
时,使用了 strlen
函数。strlen
函数是计算字符串长度的,它会遍历整个字符串直到遇到 \0
结束符。然而,在开始拼接数据之前,p
中可能不是以 \0
结束的字符串,这样使用 strlen
得到的 len
值可能是不准确的,导致拼接的位置错误,从而截断部分数据。使用一个变量来跟踪当前数据长度,而不仅仅依赖于字符串的长度。而且,每次回调函数被调用时,d 新接收的数据追加到缓冲区 p
的末尾。然而,如果 p
中已经包含了之前接收的数据,那么新的数据可能会覆盖之前的数据,导致数据丢失。应该使用一个变量来跟踪当前已经拼接的位置,并确保新接收的数据从正确的位置开始拼接。
简单来说,上面的代码的问题1,1024字节限制,2 ,使用strlen
测量不准(遇到\0的就完蛋了)
可是问题怎么解决呢,经过多方面尝试(百度哈哈哈哈),使用curl官网推荐的还是比较好的。https://curl.se/libcurl/c/CURLOPT_WRITEFUNCTION.html
经过修改完后的代码如下:
static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
size_t realsize = size * nmemb;
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
// 注意这里根据每次被调用获得的数据重新动态分配缓存区的大小
char *ptr = realloc(mem->memory, mem->size + realsize + 1);
if(ptr == NULL)
{
/* out of memory! */
Gat1400Log_Error("not enough memory (realloc returned NULL)\n");
return 0;
}
mem->memory = ptr;
memcpy(&(mem->memory[mem->size]), contents, realsize);
mem->size += realsize;
mem->memory[mem->size] = 0;
return realsize;
}
经过一天的老化,发现确实解决了接收一半数据的现象了