项目中通过ulxmlrpc实现https通信,两个线程分别运行https server端以及https client端。调试过程中发现报错:
6240:error:140A90F1:SSL routines:SSL_CTX_new:unable to load ssl2 md5 routines:.\ssl\ssl_lib.c:1804:
经排查问题为未进行库导入,需以下代码:
SSL_library_init();
SSLeay_add_ssl_algorithms();
SSL_load_error_strings();
多线程导致需进行多次库导入以及error信息导入,考虑在主线程中进行初始化或者分别在不同线程中进行初始化,在主线程中初始化会导致程序耦合性提高,不同线程中进行初始化考虑到server端进行一次初始化,client端每次使用就需进行一次初始化,需考虑是否会导致内存泄漏。
在linux环境下进行测试:
#include<iostream>
#include <string.h>
#include<pthread.h>
#include <openssl/bio.h>
#include <openssl/crypto.h>
#include <openssl/evp.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <unistd.h>
void test_ssl(void)
{
SSL_library_init();
SSLeay_add_ssl_algorithms();
SSL_load_error_strings();
}
void * test1(void*)
{
unsigned i = 0;
while(1)
{
std::cout<<"thread 1 "<< i++ <<std::endl;
SSL_METHOD *meth = (SSL_METHOD *)SSLv23_method();
SSL_CTX *ssl_ctx = SSL_CTX_new (meth);
if (!ssl_ctx)
{
std::cout<<"thread 1 error"<<std::endl;
ERR_print_errors_fp(stdout);
exit(2);
}
if (ssl_ctx != 0)
SSL_CTX_free(ssl_ctx);
ssl_ctx = 0;
sleep(1);
}
}
void * test2(void*)
{ unsigned i = 0;
while(1)
{
std::cout<<"thread 2 "<< i++ <<std::endl;
SSL_METHOD *meth = (SSL_METHOD *)SSLv23_method();
SSL_CTX *ssl_ctx = SSL_CTX_new (meth);
if (!ssl_ctx)
{
std::cout<<"thread 2 error"<<std::endl;
ERR_print_errors_fp(stdout);
exit(2);
}
if (ssl_ctx != 0)
SSL_CTX_free(ssl_ctx);
ssl_ctx = 0;
sleep(1);
}
}
void * test3(void*)
{ unsigned i = 0;
while(1)
{
std::cout<<"thread 3"<< i++ <<std::endl;
SSL_METHOD *meth = (SSL_METHOD *)SSLv23_method();
SSL_CTX *ssl_ctx = SSL_CTX_new (meth);
if (!ssl_ctx)
{
std::cout<<"thread 3 error"<<std::endl;
ERR_print_errors_fp(stdout);
exit(2);
}
if (ssl_ctx != 0)
SSL_CTX_free(ssl_ctx);
ssl_ctx = 0;
sleep(1);
}
}
int main(int argc,char *argv[])
{
test_ssl();
pthread_t t1;
pthread_t t2;
pthread_t t3;
pthread_create(&t1,NULL,test1,NULL);
pthread_create(&t2,NULL,test2,NULL);
pthread_create(&t3,NULL,test3,NULL);
while(1);
return 0;
}
测试结果发现主线程中进行统一初始化,各个线程可以正常进行SSL_CTX_new操作。
测试多线程进行初始化:
#include<iostream>
#include <string.h>
#include<pthread.h>
#include <openssl/bio.h>
#include <openssl/crypto.h>
#include <openssl/evp.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <unistd.h>
void test_ssl(void)
{
SSL_library_init();
SSLeay_add_ssl_algorithms();
SSL_load_error_strings();
}
void * test1(void*)
{
unsigned i = 0;
while(1)
{
test_ssl();
std::cout<<"thread 1 "<< i++ <<std::endl;
SSL_METHOD *meth = (SSL_METHOD *)SSLv23_method();
SSL_CTX *ssl_ctx = SSL_CTX_new (meth);
if (!ssl_ctx)
{
std::cout<<"thread 1 error"<<std::endl;
ERR_print_errors_fp(stdout);
exit(2);
}
if (ssl_ctx != 0)
SSL_CTX_free(ssl_ctx);
ssl_ctx = 0;
usleep(100);
}
}
void * test2(void*)
{ unsigned i = 0;
while(1)
{
test_ssl();
std::cout<<"thread 2 "<< i++ <<std::endl;
SSL_METHOD *meth = (SSL_METHOD *)SSLv23_method();
SSL_CTX *ssl_ctx = SSL_CTX_new (meth);
if (!ssl_ctx)
{
std::cout<<"thread 2 error"<<std::endl;
ERR_print_errors_fp(stdout);
exit(2);
}
if (ssl_ctx != 0)
SSL_CTX_free(ssl_ctx);
ssl_ctx = 0;
usleep(100);
}
}
void * test3(void*)
{ unsigned i = 0;
while(1)
{
test_ssl();
std::cout<<"thread 3"<< i++ <<std::endl;
SSL_METHOD *meth = (SSL_METHOD *)SSLv23_method();
SSL_CTX *ssl_ctx = SSL_CTX_new (meth);
if (!ssl_ctx)
{
std::cout<<"thread 3 error"<<std::endl;
ERR_print_errors_fp(stdout);
exit(2);
}
if (ssl_ctx != 0)
SSL_CTX_free(ssl_ctx);
ssl_ctx = 0;
usleep(100);
}
}
int main(int argc,char *argv[])
{
//test_ssl();
pthread_t t1;
pthread_t t2;
pthread_t t3;
pthread_create(&t1,NULL,test1,NULL);
pthread_create(&t2,NULL,test2,NULL);
pthread_create(&t3,NULL,test3,NULL);
while(1);
return 0;
}
运行测试代码top进行内存占用查看,测试进行100w次线程循环,内存未见明显增加,基本保持0.2%。
测试在一个线程中进行导入库等操作:
#include<iostream>
#include <string.h>
#include<pthread.h>
#include <openssl/bio.h>
#include <openssl/crypto.h>
#include <openssl/evp.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <unistd.h>
void test_ssl(void)
{
SSL_library_init();
SSLeay_add_ssl_algorithms();
SSL_load_error_strings();
}
void * test1(void*)
{
sleep(1);
unsigned i = 0;
while(1)
{
std::cout<<"thread 1 "<< i++ <<std::endl;
SSL_METHOD *meth = (SSL_METHOD *)SSLv23_method();
SSL_CTX *ssl_ctx = SSL_CTX_new (meth);
if (!ssl_ctx)
{
std::cout<<"thread 1 error"<<std::endl;
ERR_print_errors_fp(stdout);
exit(2);
}
if (ssl_ctx != 0)
SSL_CTX_free(ssl_ctx);
ssl_ctx = 0;
sleep(1);
}
}
void * test2(void*)
{ unsigned i = 0;
sleep(1);
while(1)
{
std::cout<<"thread 2 "<< i++ <<std::endl;
SSL_METHOD *meth = (SSL_METHOD *)SSLv23_method();
SSL_CTX *ssl_ctx = SSL_CTX_new (meth);
if (!ssl_ctx)
{
std::cout<<"thread 2 error"<<std::endl;
ERR_print_errors_fp(stdout);
exit(2);
}
if (ssl_ctx != 0)
SSL_CTX_free(ssl_ctx);
ssl_ctx = 0;
sleep(1);
}
}
void * test3(void*)
{ unsigned i = 0;
while(1)
{
test_ssl();
std::cout<<"thread 3"<< i++ <<std::endl;
SSL_METHOD *meth = (SSL_METHOD *)SSLv23_method();
SSL_CTX *ssl_ctx = SSL_CTX_new (meth);
if (!ssl_ctx)
{
std::cout<<"thread 3 error"<<std::endl;
ERR_print_errors_fp(stdout);
exit(2);
}
if (ssl_ctx != 0)
SSL_CTX_free(ssl_ctx);
ssl_ctx = 0;
sleep(1);
}
}
int main(int argc,char *argv[])
{
//test_ssl();
pthread_t t1;
pthread_t t2;
pthread_t t3;
pthread_create(&t1,NULL,test1,NULL);
pthread_create(&t2,NULL,test2,NULL);
pthread_create(&t3,NULL,test3,NULL);
while(1);
return 0;
}
测试发现线程test3先运行并进行库导入操作,其他线程依旧可以进行SSL_CTX_new申请操作。
ps:1、注意需对ssl_ctx进行释放,否则会影响测试效果。
2、g++ -g ssl_test.cpp -I /usr/local/ssl/include -L /usr/local/ssl/lib -lssl -lcrypto -ldl -pthread进行编译
3、查找参考资料导入库等操作属于全局操作,由于算法等数据结构以及插入时查找节点逻辑,多次初始化并不会导致重复节点,因此不会导致内存泄漏。
4、在window项目中在主线程中统一初始化出现部分线程可以进行SSL_CTX_new申请,部分线程无法进行SSL_CTX_new申请的现象,原因还在继续查找。
5、由于操作对象为全局变量,进行导入等操作是要考虑多线程资源保护,可使用互斥锁pthread_mutex进行保护。