openssl 多线程 线程安全 windows linux

公司某服务器的远古程序崩溃了, 分析crash dump发现是因为没解决openssl的线程安全问题,写个小demo记录一下解决方案。

openssl_multithreading_safe_windows_demo: Windows平台下演示如何解决openssl库多线程场景下线程安全问题的64位demo

openssl_lock.h

#ifndef __OPENSSL_LOCK_H__
#define __OPENSSL_LOCK_H__

//
// Windows平台下演示如何解决openssl库多线程场景下线程安全问题的64位demo
// 1.本项目为64位demo项目,32位同理
// 2.openssl存在版本差异,1_0_0前后的加解锁机制不同
// 3.需要根据系统平台差异定义OpensslProc_GetThreadId,如Windows平台下使用GetCurrentThreadId()
// 4.根据实际业务需求定义OpensslProc_LockingHandler,权衡安全性和性能
// 5.OpensslProc_InitLock、OpensslProc_CleanUpLock对应调用
//

#include <iostream>
#include <Windows.h>
//#include <pthread.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#pragma comment(lib, "libssl.lib")
#pragma comment(lib, "libcrypto.lib")

//在开始使用openssl库之前调用
void OpensslProc_InitLock(void);

//当不再使用openssl库以后调用
void OpensslProc_CleanUpLock(void);

//获取当前tid的线程回调
static unsigned long _stdcall OpensslProc_GetThreadId(void);

//给openssl内部线程资源加解锁的回调
static void _stdcall OpensslProc_LockingHandler(int mode, int type, const char* file, int line);

#endif // !__OPENSSL_LOCK_H__

openssl_lock.cpp

#include "openssl_lock.h"

using namespace std;

//Openssl全局加密锁的数量
UINT    g_uOpensslCrptLockCount = CRYPTO_num_locks();
//存储g_uOpensslCrptLockCount个互斥体句柄的数组
//每个互斥体与一个openssl内置内部资源(具体是什么资源还有待看源码研究)对应
PHANDLE g_hLock_csArray = NULL;

//获取当前tid的线程回调
static unsigned long _stdcall OpensslProc_GetThreadId(void){
    return (unsigned long)GetCurrentThreadId();
}

//给openssl内部线程资源加解锁的回调
static void _stdcall OpensslProc_LockingHandler(int mode, int type, const char* file, int line){

    if (type > g_uOpensslCrptLockCount || !g_hLock_csArray[type]){
        return;
    }

    if (mode & CRYPTO_LOCK){
        WaitForSingleObject(g_hLock_csArray[type], INFINITE);
    } else{
        ReleaseMutex(g_hLock_csArray[type]);
    }
}

//在开始使用openssl库之前调用
void OpensslProc_InitLock(void){

    //根据Openssl内部的crypto_lock的数量申请内存
    while (!g_hLock_csArray){
        g_hLock_csArray = (HANDLE*)OPENSSL_malloc(g_uOpensslCrptLockCount * sizeof(HANDLE));
    }

    for (int i = 0; i < CRYPTO_num_locks(); i++){
        while (!g_hLock_csArray[i]){
            g_hLock_csArray[i] = CreateMutex(NULL, FALSE, NULL);
        }
    }

    //register locking callback
    CRYPTO_set_locking_callback((void (*)(int, int, const char*, int))OpensslProc_LockingHandler);

    //regist tid callback
#if OPENSSL_VERSION_NUMBER >= 0x1000000f
    CRYPTO_THREADID_set_callback(OpensslProc_GetThreadId);
#else
    CRYPTO_set_id_callback(OpensslProc_GetThreadId);
#endif
}

//在openssl库使用结束后调用
void OpensslProc_CleanUpLock(void)
{
    //unregister tid callback
#if OPENSSL_VERSION_NUMBER >= 0x1000000f
    CRYPTO_THREADID_set_callback(NULL);
#else
    CRYPTO_set_id_callback(NULL);
#endif

    if (!g_hLock_csArray){
        return;
    }

    for (int i = 0; i < CRYPTO_num_locks(); i++){
        if (g_hLock_csArray[i]){
            CloseHandle(g_hLock_csArray[i]);
            g_hLock_csArray[i] = NULL;
        }
    }

    OPENSSL_free(g_hLock_csArray);
}

另外感谢这篇博客的详细讲解  多线程环境下使用openssl_yasi_xi的博客-CSDN博客_openssl 多线程

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个简单的Linux C语言程序,使用OpenSSL库实现多线程网络服务端: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include <openssl/ssl.h> #include <openssl/err.h> #define SERVER_CERT "server.crt" #define SERVER_KEY "server.key" #define PORT 4433 #define BACKLOG 10 void* handle_client(void* arg) { SSL* ssl = (SSL*) arg; char buffer[1024]; int bytes; const char* welcome = "Welcome to the server!\n"; SSL_write(ssl, welcome, strlen(welcome)); while (1) { bytes = SSL_read(ssl, buffer, sizeof(buffer)); if (bytes > 0) { buffer[bytes] = '\0'; printf("[Client]: %s", buffer); if (strcmp(buffer, "bye\n") == 0) { const char* bye = "Goodbye!\n"; SSL_write(ssl, bye, strlen(bye)); break; } } } SSL_shutdown(ssl); SSL_free(ssl); close((intptr_t) arg); pthread_exit(NULL); } int main() { SSL_CTX* ctx; pthread_t tid; int server_fd, client_fd; SSL_library_init(); OpenSSL_add_all_algorithms(); SSL_load_error_strings(); ctx = SSL_CTX_new(TLSv1_2_server_method()); if (!ctx) { printf("Failed to create SSL context\n"); exit(EXIT_FAILURE); } if (SSL_CTX_use_certificate_file(ctx, SERVER_CERT, SSL_FILETYPE_PEM) < 0) { printf("Failed to load server certificate\n"); exit(EXIT_FAILURE); } if (SSL_CTX_use_PrivateKey_file(ctx, SERVER_KEY, SSL_FILETYPE_PEM) < 0 ) { printf("Failed to load server private key\n"); exit(EXIT_FAILURE); } server_fd = socket(AF_INET, SOCK_STREAM, 0); if (server_fd < 0) { printf("Failed to create socket\n"); exit(EXIT_FAILURE); } struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; server_addr.sin_port = htons(PORT); if (bind(server_fd, (struct sockaddr*) &server_addr, sizeof(server_addr)) < 0) { printf("Failed to bind socket\n"); exit(EXIT_FAILURE); } if (listen(server_fd, BACKLOG) < 0) { printf("Failed to listen on socket\n"); exit(EXIT_FAILURE); } while (1) { client_fd = accept(server_fd, NULL, NULL); if (client_fd < 0) { printf("Failed to accept client connection\n"); continue; } SSL* ssl = SSL_new(ctx); SSL_set_fd(ssl, client_fd); if (SSL_accept(ssl) <= 0) { printf("Failed to establish SSL connection\n"); SSL_free(ssl); close(client_fd); continue; } pthread_create(&tid, NULL, handle_client, ssl); pthread_detach(tid); } SSL_CTX_free(ctx); close(server_fd); return 0; } ``` 这个程序会创建一个SSL/TLS加密的网络服务端,使用4433端口监听客户端连接。每当有一个新的客户端连接成功后,程序会为其创建一个新的线程来处理客户端请求。客户端与服务端之间的通信都会使用SSL/TLS进行加密。 注意:程序中的服务器证书和私钥文件需要根据实际情况进行替换。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值