libcurl 多线程使用注意事项(补充)——HTTPS,openssl多线程使用加锁

问题

多线程libcurl运行一段时间后出现崩掉,没有确定的点,没有确定的URL。一直查看源代码没有问题,最后通过debug跟踪发现是在访问SSL的时候出现的crash。

才想起来openssl是不支持多线程的,要自己做加锁处理。而且libcurl中并没有支持相关的加锁操作。


解决办法:

在初始化libcurl的时候为openssl创建一个互斥锁函数,一个回调函数传给openss

openssl锁l函数原形 :void (* func )(int ,int , const char * ,int)

设置方式:CRYPTO_set_locking_callback(void (* func )(int ,int , const char * ,int));

设置这样一个函数还不够,另外还要配置一个锁id回调函数,这个可以参考openssl多线程下的使用相关。

id函数原形:unsigned int (*func)(void)

设置方式:CRYPTO_set_id_callback(unsigned int (*func)(void));

通过这两个设置就可以解决HTTPS多线程请求出现crash的问题。


代码示例:

下面是引用了libcurl示例的一个代码

最关键就是,两个callback的实现,还有初始化锁(init_locks)和释放锁(kill_locks)的位置

#define USE_OPENSSL 
       
    #include <stdio.h> 
    #include <pthread.h> 
    #include <curl/curl.h> 
       
    #define NUMT 4 
       
    /* we have this global to let the callback get easy access to it */ 
    static pthread_mutex_t *lockarray; 
       
    #ifdef USE_OPENSSL 
    #include <openssl/crypto.h> 
    static void lock_callback(int mode, int type, char *file, int line) 
    { 
      (void)file; 
      (void)line; 
      if (mode & CRYPTO_LOCK) { 
        pthread_mutex_lock(&(lockarray[type])); 
      } 
      else { 
        pthread_mutex_unlock(&(lockarray[type])); 
      } 
    } 
       
    static unsigned long thread_id(void) 
    { 
      unsigned long ret; 
       
      ret=(unsigned long)pthread_self(); 
      return(ret); 
    } 
       
    static void init_locks(void) 
    { 
      int i; 
       
      lockarray=(pthread_mutex_t *)OPENSSL_malloc(CRYPTO_num_locks() * 
                                                sizeof(pthread_mutex_t)); 
      for (i=0; i<CRYPTO_num_locks(); i++) { 
        pthread_mutex_init(&(lockarray[i]),NULL); 
      } 
       
      CRYPTO_set_id_callback((unsigned long (*)())thread_id); 
      CRYPTO_set_locking_callback((void (*)())lock_callback); 
    } 
       
    static void kill_locks(void) 
    { 
      int i; 
       
      CRYPTO_set_locking_callback(NULL); 
      for (i=0; i<CRYPTO_num_locks(); i++) 
        pthread_mutex_destroy(&(lockarray[i])); 
       
      OPENSSL_free(lockarray); 
    } 
    #endif 
       
    #ifdef USE_GNUTLS 
    #include <gcrypt.h> 
    #include <errno.h> 
       
    GCRY_THREAD_OPTION_PTHREAD_IMPL; 
       
    void init_locks(void) 
    { 
      gcry_control(GCRYCTL_SET_THREAD_CBS); 
    } 
       
    #define kill_locks() 
    #endif 
       
    /* List of URLs to fetch.*/ 
    const char * const urls[]= { 
      "https://www.example.com/", 
      "https://www2.example.com/", 
      "https://www3.example.com/", 
      "https://www4.example.com/", 
    }; 
       
    static void *pull_one_url(void *url) 
    { 
      CURL *curl; 
       
      curl = curl_easy_init(); 
      curl_easy_setopt(curl, CURLOPT_URL, url); 
      /* this example doesn't verify the server's certificate, which means we 
         might be downloading stuff from an impostor */ 
      curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); 
      curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); 
      curl_easy_perform(curl); /* ignores error */ 
      curl_easy_cleanup(curl); 
       
      return NULL; 
    } 
       
    int main(int argc, char **argv) 
    { 
      pthread_t tid[NUMT]; 
      int i; 
      int error; 
      (void)argc; /* we don't use any arguments in this example */ 
      (void)argv; 
       
      /* Must initialize libcurl before any threads are started */ 
      curl_global_init(CURL_GLOBAL_ALL); 
       
      init_locks(); 
       
      for(i=0; i< NUMT; i++) { 
        error = pthread_create(&tid[i], 
                               NULL, /* default attributes please */ 
                               pull_one_url, 
                               (void *)urls[i]); 
        if(0 != error) 
          fprintf(stderr, "Couldn't run thread number %d, errno %d\n", i, error); 
        else 
          fprintf(stderr, "Thread %d, gets %s\n", i, urls[i]); 
      } 
       
      /* now wait for all threads to terminate */ 
      for(i=0; i< NUMT; i++) { 
        error = pthread_join(tid[i], NULL); 
        fprintf(stderr, "Thread %d terminated\n", i); 
      } 
       
      kill_locks(); 
       
      return 0; 
    }
#define USE_OPENSSL 
       
    #include <stdio.h> 
    #include <pthread.h> 
    #include <curl/curl.h> 
       
    #define NUMT 4 
       
    /* we have this global to let the callback get easy access to it */ 
    static pthread_mutex_t *lockarray; 
       
    #ifdef USE_OPENSSL 
    #include <openssl/crypto.h> 
    static void lock_callback(int mode, int type, char *file, int line) 
    { 
      (void)file; 
      (void)line; 
      if (mode & CRYPTO_LOCK) { 
        pthread_mutex_lock(&(lockarray[type])); 
      } 
      else { 
        pthread_mutex_unlock(&(lockarray[type])); 
      } 
    } 
       
    static unsigned long thread_id(void) 
    { 
      unsigned long ret; 
       
      ret=(unsigned long)pthread_self(); 
      return(ret); 
    } 
       
    static void init_locks(void) 
    { 
      int i; 
       
      lockarray=(pthread_mutex_t *)OPENSSL_malloc(CRYPTO_num_locks() * 
                                                sizeof(pthread_mutex_t)); 
      for (i=0; i<CRYPTO_num_locks(); i++) { 
        pthread_mutex_init(&(lockarray[i]),NULL); 
      } 
       
      CRYPTO_set_id_callback((unsigned long (*)())thread_id); 
      CRYPTO_set_locking_callback((void (*)())lock_callback); 
    } 
       
    static void kill_locks(void) 
    { 
      int i; 
       
      CRYPTO_set_locking_callback(NULL); 
      for (i=0; i<CRYPTO_num_locks(); i++) 
        pthread_mutex_destroy(&(lockarray[i])); 
       
      OPENSSL_free(lockarray); 
    } 
    #endif 
       
    #ifdef USE_GNUTLS 
    #include <gcrypt.h> 
    #include <errno.h> 
       
    GCRY_THREAD_OPTION_PTHREAD_IMPL; 
       
    void init_locks(void) 
    { 
      gcry_control(GCRYCTL_SET_THREAD_CBS); 
    } 
       
    #define kill_locks() 
    #endif 
       
    /* List of URLs to fetch.*/ 
    const char * const urls[]= { 
      "https://www.example.com/", 
      "https://www2.example.com/", 
      "https://www3.example.com/", 
      "https://www4.example.com/", 
    }; 
       
    static void *pull_one_url(void *url) 
    { 
      CURL *curl; 
       
      curl = curl_easy_init(); 
      curl_easy_setopt(curl, CURLOPT_URL, url); 
      /* this example doesn't verify the server's certificate, which means we 
         might be downloading stuff from an impostor */ 
      curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); 
      curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); 
      curl_easy_perform(curl); /* ignores error */ 
      curl_easy_cleanup(curl); 
       
      return NULL; 
    } 
       
    int main(int argc, char **argv) 
    { 
      pthread_t tid[NUMT]; 
      int i; 
      int error; 
      (void)argc; /* we don't use any arguments in this example */ 
      (void)argv; 
       
      /* Must initialize libcurl before any threads are started */ 
      curl_global_init(CURL_GLOBAL_ALL); 
       
      init_locks(); 
       
      for(i=0; i< NUMT; i++) { 
        error = pthread_create(&tid[i], 
                               NULL, /* default attributes please */ 
                               pull_one_url, 
                               (void *)urls[i]); 
        if(0 != error) 
          fprintf(stderr, "Couldn't run thread number %d, errno %d\n", i, error); 
        else 
          fprintf(stderr, "Thread %d, gets %s\n", i, urls[i]); 
      } 
       
      /* now wait for all threads to terminate */ 
      for(i=0; i< NUMT; i++) { 
        error = pthread_join(tid[i], NULL); 
        fprintf(stderr, "Thread %d terminated\n", i); 
      } 
       
      kill_locks(); 
       
      return 0; 
    }


你可能感兴趣的:(libcurl 多线程使用注意事项(补充)——HTTPS,openssl多线程使用加锁)