在 c 中使用线程的 Curl 客户端会产生随机行为吗?

标签 c xml curl pthreads libxml2

我们正在使用 mongoose 为特定项目开发异步服务器。它接受一个请求,立即做出响应,进行所需的所有解析,然后响应原始请求中传递的 URL。

请求接收部分完美无缺。不过,响应是另一回事。

不能将整个程序放在这里(礼节问题),但我已经能够制作一个副本,以便可以证明该问题。

/*
 * xmlcrlth.c
 *
 *  Created on: Jun 28, 2012
 *      Author: mfaraz
 */

#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <curl/curl.h>
#include <libxml2/libxml/xmlreader.h>
#include <libxml2/libxml/encoding.h>
#include <libxml2/libxml/xmlwriter.h>

#define MAX_DATA_LEN            (140)
#define MAX_XML_LEN             (1024)
#define MAX_THREAD_STACK        (1024 * 1024)
//#define MY_ENCODING "ISO-8859-1"
#define MY_ENCODING "UTF-8"
#define DEFAULT_THREADS         25

//typedef
struct responseInfo {
    pthread_t   thread_id;              /*  ID returned by pthread_create() */
    int         urlLength;              /*  The Length of the URL   */
    int         xmlLength;              /*  The Length of the XML   */
    char        theXML[MAX_XML_LEN];    /*  The XML to post */
    char        theURL[MAX_DATA_LEN];   /*  The URL to post to  */
};

static void *threadCurl(void *newResponse)  {
    pthread_detach(pthread_self());
    struct responseInfo *thisReponse = (struct responseInfo *) newResponse;
    char thisXML[MAX_XML_LEN], thisURL[MAX_DATA_LEN];
    CURL *curlResponse;     /*  cURL variable to perform a Response */
    CURLcode curlResCode;   /*  cURL Response Code  */
    struct curl_slist *headList = NULL;
    int urlLength = strlen(thisReponse->theURL);    int xmlLength = strlen(thisReponse->theXML);
    printf("Received XML (%d vs %d bytes) using ThreadID %lu\n%s \nto URL (%d vs %d bytes) %s\n", xmlLength, thisReponse->xmlLength, thisReponse->thread_id, thisReponse->theXML, urlLength, thisReponse->urlLength, thisReponse->theURL);
//  if (urlLength != thisReponse->urlLength)
        strncpy(thisURL, thisReponse->theURL, thisReponse->urlLength);
//  if (xmlLength != thisReponse->xmlLength)
        strncpy(thisXML, thisReponse->theXML, thisReponse->xmlLength);
    /*FILE *filePtr;    char fileName[MAX_DATA_LEN] = "";   sprintf(fileName, "%lu", pthread_self());
    filePtr = fopen(fileName, "a+");
    fprintf(filePtr, "-------------------------\n");
    fprintf(filePtr, "ThreadID: %s\n", fileName);
    fprintf(filePtr, "OurlLength: %d\tCurlLength: %d\n", thisReponse->urlLength, urlLength);
    fprintf(filePtr, "OxmlLength: %d\tCxmlLength: %d\n\n", thisReponse->xmlLength, xmlLength);
    fprintf(filePtr, "OrigURL: %s\n", thisReponse->theURL);
    fprintf(filePtr, "OrigXML: %s\n", thisReponse->theXML);
    fprintf(filePtr, "NewURL: %s\n", thisURL);
    fprintf(filePtr, "NewXML: %s\n", thisXML);
    fclose(filePtr);//*/
    urlLength = strlen(thisURL);    xmlLength = strlen(thisXML);
    printf("Sending XML (%d bytes) using ThreadID %lu\n%s \nto URL (%d bytes) %s\n", xmlLength, thisReponse->thread_id, thisXML, urlLength, thisURL);
    headList = curl_slist_append(headList, "Content-Type: text/xml");
    curlResponse = curl_easy_init();
    if (curlResponse)   {
        curl_easy_setopt(curlResponse, CURLOPT_URL, thisURL);
        curl_easy_setopt(curlResponse, CURLOPT_HTTPHEADER, headList);
//      curl_easy_setopt(curlResponse, CURLOPT_POSTFIELDS, xmlResp);
        curl_easy_setopt(curlResponse, CURLOPT_POSTFIELDSIZE, thisReponse->xmlLength);
        curl_easy_setopt(curlResponse, CURLOPT_COPYPOSTFIELDS, thisXML);
        curl_easy_setopt(curlResponse, CURLOPT_CONNECTTIMEOUT, 10);
        curl_easy_setopt(curlResponse, CURLOPT_TIMEOUT, 30);
        curl_easy_setopt(curlResponse, CURLOPT_NOSIGNAL, 1);
        curl_easy_setopt(curlResponse, CURLOPT_FRESH_CONNECT, 1);
        curl_easy_setopt(curlResponse, CURLOPT_FORBID_REUSE, 1);
        curlResCode = curl_easy_perform(curlResponse);
        if (curlResCode)    printf("Curl Failed with status: %s!\n", curl_easy_strerror(curlResCode));
        curl_easy_cleanup(curlResponse);
    }   else    {
        printf("Curl Init Failed!\n");
        curlResCode = CURLE_FAILED_INIT;
    }
    curl_slist_free_all(headList);
    free(thisReponse);
    pthread_exit(NULL);
//  return (int)curlResCode;
}

char *getXML(int failCode, char *failDesc)  {
    int rc;
    xmlTextWriterPtr writer;
    xmlBufferPtr buf;
//    xmlChar *tmp;
    static char xmlResp[1024];  xmlResp[0] = '\0';
    /* Create a new XML buffer, to which the XML document will be written */
    buf = xmlBufferCreate();    if (buf == NULL)    printf("MTU_smac: Error creating the xml buffer\n");
    /* Create a new XmlWriter for memory, with no compression.
     * Remark: there is no compression for this kind of xmlTextWriter */
    writer = xmlNewTextWriterMemory(buf, 0);    if (writer == NULL) printf("MTU_smac: Error creating the xml writer\n");
    /* Start the document with the xml default for the version,
     * encoding ISO 8859-1 and the default for the standalone
     * declaration. */
    rc = xmlTextWriterStartDocument(writer, NULL, MY_ENCODING, NULL);   if (rc < 0) printf("MTU_smac: Error at xmlTextWriterStartDocument\n");
    /* Start an element named "svc_result". Since thist is the first
     * element, this will be the root element of the document. */
    rc = xmlTextWriterStartElement(writer, (xmlChar *)"svc_result");    if (rc < 0) printf("MTU_smac: Error at xmlTextWriterStartElement\n");
    /* Start an element named "sli_rep" as child of svc_result. */
    rc = xmlTextWriterStartElement(writer, (xmlChar *)"sli_rep");   if (rc < 0) printf("MTU_smac: Error at xmlTextWriterStartElement\n");
    /* Add an attribute with name "version" and value "1.0" to sli_rep. */
    rc = xmlTextWriterWriteAttribute(writer, (xmlChar *)"version", (xmlChar *)"3.0.0"); if (rc < 0) printf("MTU_smac: Error at xmlTextWriterWriteAttribute\n");
    /* Start an element named "pos" as child of sli_rep. */
    rc = xmlTextWriterStartElement(writer, (xmlChar *)"pos");   if (rc < 0) printf("MTU_smac: Error at xmlTextWriterStartElement\n");
    /* Write an element named "poserr" as child of pos. */
    rc = xmlTextWriterStartElement(writer, (xmlChar *)"poserr");    if (rc < 0) printf("MTU_smac: Error at xmlTextWriterStartElement\n");
    /* Write an element named "time" as child of poserr. */
    rc = xmlTextWriterWriteElement(writer, (xmlChar *)"time", (xmlChar *)"Time will Come here");    if (rc < 0) printf("MTU_smac: Error at xmlTextWriterWriteFormatElement\n");
    /* Add an attribute with name "utc" and value "+0500" to time. */
    rc = xmlTextWriterWriteAttribute(writer, (xmlChar *)"utc", (xmlChar *)"+0500"); if (rc < 0) printf("MTU_smac: Error at xmlTextWriterWriteAttribute for utc\n");
    /* Write an element named "result" as child of poserr. */
    rc = xmlTextWriterWriteFormatElement(writer, (xmlChar *)"result", "%s", failDesc);  if (rc < 0) printf("MTU_smac: Error at xmlTextWriterWriteFormatElement\n");
    /* Add an attribute with name "utc" and value "+0500" to time. */
    rc = xmlTextWriterWriteAttribute(writer, (xmlChar *)"resid", (xmlChar *)failCode);  if (rc < 0) printf("MTU_smac: Error at xmlTextWriterWriteAttribute for resid\n");
    /* Close the element named poserr. */
    rc = xmlTextWriterEndElement(writer);   if (rc < 0) printf("MTU_smac: Error at xmlTextWriterEndElement\n");
    /* Close the element named pos. */
    rc = xmlTextWriterEndElement(writer);   if (rc < 0) printf("MTU_smac: Error at xmlTextWriterEndElement\n");
    /* Start an element named "extra" as child of sli_rep. */
    rc = xmlTextWriterStartElement(writer, (xmlChar *)"extra"); if (rc < 0) printf("MTU_smac: Error at xmlTextWriterStartElement\n");
    /* Write an element named "resp_status" as child of extra. */
    rc = xmlTextWriterWriteFormatElement(writer, (xmlChar *)"resp_status", "%d", failCode); if (rc < 0) printf("MTU_smac: Error at xmlTextWriterWriteFormatElement\n");
    /* Here we could close the elements sli_rep and svc_result using the
     * function xmlTextWriterEndElement, but since we do not want to
     * write any other elements, we simply call xmlTextWriterEndDocument,
     * which will do all the work. */
    rc = xmlTextWriterEndDocument(writer);
    if (rc < 0) printf("MTU_smac: Error at xmlTextWriterEndDocument\n");
    xmlFreeTextWriter(writer);
//    printf("%s\n", (const char *) buf->content);
    sprintf(xmlResp, "%s", (const char *) buf->content);
    xmlBufferFree(buf);
    return xmlResp;
}

int postXMLResponse(char *xmlResp, char *theURL)    {
    int urlLength = strlen(theURL);
    if (urlLength == 0) return 0;   //  No URL...no Tension...!
    struct responseInfo *newResponse;
    pthread_attr_t theAttribs;
    int threadPtr;
    //  Init the threading
    threadPtr = pthread_attr_init(&theAttribs);
    if (threadPtr)  return threadPtr;
    else    printf("Thread Initialized!\n");
    //  Set the Stack size.
    threadPtr = pthread_attr_setstacksize(&theAttribs, MAX_THREAD_STACK);
    if (threadPtr)  return threadPtr;
    else    printf("Thread Stack Size set!\n");
    //  Set the Thread as Detachable.
    threadPtr = pthread_attr_setdetachstate(&theAttribs, PTHREAD_CREATE_DETACHED);
    if (threadPtr)  return threadPtr;
    //  Allocate memory for the responseInfo
    newResponse = malloc(sizeof(struct responseInfo));
    if (newResponse == NULL)    return -1;
    else    printf("Structure MALLOC'ed!\n");
    int xmlLength = strlen(xmlResp);    int xmlLength2 = xmlLength;//293;
    newResponse->urlLength = urlLength;
    newResponse->xmlLength = xmlLength;
    strncpy(newResponse->theURL, theURL, urlLength);
    strncpy(newResponse->theXML, xmlResp, xmlLength2);
    printf("Threading Send XML (%d bytes)\n%s \nto URL (%d bytes) %s\n", xmlLength, newResponse->theXML, urlLength, newResponse->theURL);
    threadPtr = pthread_create(&newResponse->thread_id, &theAttribs, &threadCurl, newResponse);
    if (threadPtr)  return threadPtr;
    else    printf("Thread Created with ThreadID %lu!\n", newResponse->thread_id);
    /*FILE *filePtr;    char fileName[MAX_DATA_LEN] = "";   sprintf(fileName, "%lu", newResponse->thread_id);
    filePtr = fopen(fileName, "a");
    fprintf(filePtr, "ThreadID: %s\n", fileName);
    fprintf(filePtr, "urlLength: %d\n", newResponse->urlLength);
    fprintf(filePtr, "xmlLength: %d\n", newResponse->xmlLength);
    fprintf(filePtr, "URL: %s\n", newResponse->theURL);
    fprintf(filePtr, "XML: %s", newResponse->theXML);
    fclose(filePtr);//*/
    threadPtr = pthread_attr_destroy(&theAttribs);
    if (threadPtr)  return threadPtr;
    else    printf("Thread Attributes!\n");
    return 0;
}

int main(int argc, char *argv[])    {
    long int iLoop, iMaxCount;
    curl_global_init(CURL_GLOBAL_ALL);
    iMaxCount = DEFAULT_THREADS;
    if (argc > 1)   {
//      printf("Showing Arguments: -\n");   for (iLoop = 1 ; iLoop < argc ; iLoop++)    printf("%lu = %s\n", iLoop + 1, argv[iLoop]);
        iMaxCount = strtol(argv[1], NULL, 10);
    }
    for (iLoop = 0 ; iLoop < iMaxCount ; iLoop++)   {
        postXMLResponse(getXML(iLoop, "All of your base are belong to us!"), "http://localhost:80/response.php");
    }
    sleep(3);
    return 0;
}

您需要为库设置“xml2”、“curl”和“pthread”,为链接器设置“-pthread”。根据您的设置,您可能还需要为 libxml 库指定实际文件夹。

当您运行该程序时,它会尝试生成 25 个线程。第一个参数可以将其更改为适合您进行测试的任何数字。在我的测试中,即使是 5,也只会导致 3 个请求登陆网络服务器。即使在这 3 个中,一个有损坏的 URL,一个有损坏的 XML。

我知道我在等待线程结束时遗漏了一些东西(无法弄清楚,因为主服务器不会停止)。

有人指出这里有问题!

最佳答案

您可能希望更正确地初始化您的字符串。

将以下补丁应用于 OP 的代码可以使其编译并使输出看起来更合适:

--- main.c.orig 2012-07-07 14:11:41.000000000 +0200
+++ main.c      2012-07-07 14:09:10.000000000 +0200
@@ -4,15 +4,20 @@
  *  Created on: Jun 28, 2012
  *      Author: mfaraz
  */
+#define LIBXML_WRITER_ENABLED

+#include <unistd.h>
 #include <pthread.h>
 #include <string.h>
+#include <stdlib.h>
 #include <stdio.h>
 #include <time.h>
 #include <curl/curl.h>
+#include <libxml2/libxml/xmlstring.h>
 #include <libxml2/libxml/xmlreader.h>
 #include <libxml2/libxml/encoding.h>
 #include <libxml2/libxml/xmlwriter.h>
+#include <libxml2/libxml/tree.h>

 #define MAX_DATA_LEN            (140)
 #define MAX_XML_LEN             (1024)
@@ -33,7 +38,7 @@
 static void *threadCurl(void *newResponse)  {
     pthread_detach(pthread_self());
     struct responseInfo *thisReponse = (struct responseInfo *) newResponse;
-    char thisXML[MAX_XML_LEN], thisURL[MAX_DATA_LEN];
+    char thisXML[MAX_XML_LEN]="", thisURL[MAX_DATA_LEN]="";
     CURL *curlResponse;     /*  cURL variable to perform a Response */
     CURLcode curlResCode;   /*  cURL Response Code  */
     struct curl_slist *headList = NULL;
@@ -155,7 +160,7 @@
     threadPtr = pthread_attr_setdetachstate(&theAttribs, PTHREAD_CREATE_DETACHED);
     if (threadPtr)  return threadPtr;
     //  Allocate memory for the responseInfo
-    newResponse = malloc(sizeof(struct responseInfo));
+    newResponse = calloc(1, sizeof(struct responseInfo));
     if (newResponse == NULL)    return -1;
     else    printf("Structure MALLOC'ed!\n");
     int xmlLength = strlen(xmlResp);    int xmlLength2 = xmlLength;//293;

顺便说一句:开始考虑一种不同类型的源代码布局是个好主意(看起来您正在使用 Eclipse,我建议您使用 Ctrl-Shift-F 魔术组合键……;-) ).


此外,以下行不能确保要被 printf()ed 的内容还没有被先前生成的线程函数再次 free()ed:

else    printf("Thread Created with ThreadID %lu!\n", newResponse->thread_id);

如果 newResponse 已经被free()ed,您的应用程序将遇到未定义的行为。

关于在 c 中使用线程的 Curl 客户端会产生随机行为吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11248009/

相关文章:

c - 尝试反转 char 数组时出错

带 pipe 和 fork 的 C 作业

android - XmlPullParser - 从具有特定属性值的标签获取数据

rest - 如何使用 cURL 发出 PUT 请求?

c - 从 C 中的 char 中提取文本

c - 如果我在分配指针之前不释放它会发生什么

android - 为 Android View 设置 OnClick()

java - 是否有任何 Java HTML 解析器生成的节点保留原始文本的索引?

java - 为什么curl在Java中运行时会失败?

php - iCloud CalDAV 通过 PHP