SSL_connect 连接超时的处理方法

问题:一个简单的用例分析。

环境:

[root@localhost ~/test/ssl_test]
# uname -a
Linux localhost.localdomain 3.10.0-123.el7.x86_64 #1 SMP Mon Jun 30 12:09:22 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

拓扑:localhost
±---------------------------------------------------------------+
client ---------127.0.0.1/2020----------> server
±---------------------------------------------------------------+

Server端:
借助于nc工具;

Client端:

#include <stdio.h>
#include <string.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

static void test_ssl_socket(int socket)
{
    char buf[8192] = {0};
    int ret = 0;

    SSL_library_init();
    SSL_load_error_strings();

    SSL_CTX *ctx = SSL_CTX_new(SSLv23_client_method());
    if (ctx) {
        SSL *ssl = SSL_new(ctx);

        if (ssl) {
            ret = SSL_set_fd(ssl, socket);

            printf("SSL_set_fd: %d\n", ret);

            ret = SSL_connect(ssl);

            printf("SSL_connect: %d\n", ret);
                
            printf("======send=====\n");
            strncpy(buf, "I am client", strlen(buf));
            ret = SSL_write(ssl, buf, strlen(buf));
            printf("%d bytes sent\n", ret);

            printf("======recv=====\n");
            memset(buf, 0, sizeof(buf));
            ret = SSL_read(ssl, buf, sizeof(buf));
            printf("%d bytes read\n", ret);

            if (ret > 0) {
                printf("%s\n", buf);
            }   

            SSL_free(ssl);
        }
        SSL_CTX_free(ctx);
    }
}

int main(int argc, char *const argv[])
{
    int socket_fd = -1;
    const char *ip = NULL;
    unsigned short port = 0;
    struct sockaddr_in svraddr;
    int ret = -1;

    ip = argv[1];
    port = atoi(argv[2]);

    printf("IP: %s, port: %d\n", argv[1], port);

    /* 创建一个 socket 用于 tcp 通信 */
    socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (socket < 0) {
        perror("Error: Failed to create socket\n");
        exit(errno);
    }

    printf("socket created\n");

    bzero(&svraddr, sizeof(svraddr));
    svraddr.sin_family = AF_INET;
    svraddr.sin_addr.s_addr = inet_addr(ip);
    svraddr.sin_port = htons(port);

    ret = connect(socket_fd, (struct sockaddr *)&svraddr, sizeof(svraddr));
    if (ret < 0) {
        printf("create socket failed\n");
        return -1;
    }

    test_ssl_socket(socket_fd);
    printf("OK\n");
    return 0;
}

编译:

[root@localhost ~/test/ssl_test]
# gcc ./ssl_client.c -o ssl_client -lssl

运行:
(1)server执行

# nc -l 2020

(2)client执行

# ./ssl_client 127.0.0.1 2021
IP: 127.0.0.1, port: 2021
socket created
SSL_set_fd: 1

/*长时间等待...*/

问题:
Q1:客户端为什么长时间等待?
A1:客户端停留在SSL_connect()处,并尝试建立ssl连接,对端使用nc开启非ssl连接,导致client端ssl连接失败。

Q2:客户端如果连接不成功,是否可以超时退出。
A2: 提供一个思路,使用定时器,当定时器时间到达时,在定时器的回调函数中释放ssl和ctx,这样在SSL_connect()中会及时返回。

#include <stdio.h>
#include <string.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <unistd.h>
#include <signal.h>


int socket_fd = -1;
SSL_CTX *ctx = NULL;
SSL *ssl = NULL;

static void test_ssl_socket(int socket)
{
    char buf[8192] = {0};
    int ret = 0;

    SSL_library_init();
    SSL_load_error_strings();

    ctx = SSL_CTX_new(SSLv23_client_method());
    if (ctx) {
        ssl = SSL_new(ctx);

        if (ssl) {
            ret = SSL_set_fd(ssl, socket);

            printf("SSL_set_fd: %d\n", ret);

            ret = SSL_connect(ssl);
            printf("SSL_connect: %d\n", ret);
            if (ret < 0) {
                printf("Error: Failed to create ssl connect\n");
                return;
            }

            printf("SSL_connect: %d\n", ret);
            
            printf("======send=====\n");
            strncpy(buf, "I am client", strlen(buf));
            ret = SSL_write(ssl, buf, strlen(buf));
            printf("%d bytes sent\n", ret);

            printf("======recv=====\n");
            memset(buf, 0, sizeof(buf));
            ret = SSL_read(ssl, buf, sizeof(buf));
            printf("%d bytes read\n", ret);

            if (ret > 0) {
                printf("%s\n", buf);
            }
            if (ssl != NULL) {
                printf("free SSL.\n");
                SSL_free(ssl);
            }
        }
        if (ctx != NULL) {
            printf("free CTX.\n");
            SSL_CTX_free(ctx);
        }
    }
}

void free_ssl_connect(int sig)
{
    printf("10s reached.\n");
    printf("free SSL.\n");
    SSL_free(ssl);
    printf("free CTX.\n");
    SSL_CTX_free(ctx);
}

int main(int argc, char *const argv[])
{
    const char *ip = NULL;
    unsigned short port = 0;
    struct sockaddr_in svraddr;
    int ret = -1;

    /***timer***/
    signal(SIGALRM, free_ssl_connect);
    alarm(10);
    /******/

    ip = argv[1];
    port = atoi(argv[2]);

    printf("IP: %s, port: %d\n", argv[1], port);

    /* 创建一个 socket 用于 tcp 通信 */
    socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (socket < 0) {
        perror("Error: Failed to create socket\n");
        exit(errno);
    }

    printf("socket created\n");

    bzero(&svraddr, sizeof(svraddr));
    svraddr.sin_family = AF_INET;
    svraddr.sin_addr.s_addr = inet_addr(ip);
    svraddr.sin_port = htons(port);

    ret = connect(socket_fd, (struct sockaddr *)&svraddr, sizeof(svraddr));
    if (ret < 0) {
        printf("Error:Create socket failed\n"); 
        return -1;
    }

    test_ssl_socket(socket_fd);
    printf("free fd.\n");
    if (socket_fd != -1) {
        printf("free fd\n");
        close(socket_fd);
    }
    printf("OK\n");
    return 0;
}

优化:

#include <stdio.h>
#include <string.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <unistd.h>
#include <signal.h>


int socket_fd = -1;
SSL_CTX *ctx = NULL;
SSL *ssl = NULL;

static void free_ssl_connect(int sig)
{
    if (sig != SIGALRM) {
        printf("sig != SIGALRM\n");
        return ;
    }
    printf("10s timer reached.\n");
    if (ssl != NULL) {
        printf("free SSL.\n");
        SSL_free(ssl);
    }
    if (ctx != NULL) {
    
        printf("free CTX.\n");
        SSL_CTX_free(ctx);
    }
}

static void close_ssl_connect_timer(void)
{
    printf("close 10s timer\n");
    alarm(0);
}

static void open_ssl_connect_timer(void)
{
    /***timer***/
    printf("open 10s timer\n");
    signal(SIGALRM, free_ssl_connect);
    alarm(10);
}

static void test_ssl_socket(int socket)
{
    char buf[8192] = {0};
    int ret = 0;

    SSL_library_init();
    SSL_load_error_strings();

    ctx = SSL_CTX_new(SSLv23_client_method());
    if (ctx) {
        ssl = SSL_new(ctx);

        if (ssl) {
            ret = SSL_set_fd(ssl, socket);

            printf("SSL_set_fd: %d\n", ret);

            /*open ssl connect timer.*/
            open_ssl_connect_timer();
            ret = SSL_connect(ssl);
            printf("SSL_connect: %d\n", ret);
            if (ret < 0) {
                printf("return\n");
                return;
            }

            /*close ssl connect timer.*/
            close_ssl_connect_timer();

            printf("SSL_connect: %d\n", ret);
            
            printf("======send=====\n");
            strncpy(buf, "I am client", strlen(buf));
            ret = SSL_write(ssl, buf, strlen(buf));
            printf("%d bytes sent\n", ret);

            printf("======recv=====\n");
            memset(buf, 0, sizeof(buf));
            ret = SSL_read(ssl, buf, sizeof(buf));
            printf("%d bytes read\n", ret);

            if (ret > 0) {
                printf("%s\n", buf);
            }
            if (ssl != NULL) {
                printf("free SSL.\n");
                SSL_free(ssl);
                ssl = NULL;
            }
        }
        if (ctx != NULL) {
            printf("free CTX.\n");
            SSL_CTX_free(ctx);
            ctx = NULL;
        }
    }
}

int main(int argc, char *const argv[])
{
    const char *ip = NULL;
    unsigned short port = 0;
    struct sockaddr_in svraddr;
    int ret = -1;

    if (argc != 3) {
        printf("Error: params error\n");
        return -1;
    }

    ip = argv[1];
    port = atoi(argv[2]);

    printf("IP: %s, port: %d\n", argv[1], port);

    /* 创建一个 socket 用于 tcp 通信 */
    socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (socket < 0) {
        perror("Error: Failed to create socket\n");
        exit(errno);
    }

    printf("socket created\n");

    bzero(&svraddr, sizeof(svraddr));
    svraddr.sin_family = AF_INET;
    svraddr.sin_addr.s_addr = inet_addr(ip);
    svraddr.sin_port = htons(port);

    ret = connect(socket_fd, (struct sockaddr *)&svraddr, sizeof(svraddr));
    if (ret < 0) {
        printf("create socket failed\n"); 
        return -1;
    }

    test_ssl_socket(socket_fd);
    printf("free fd.\n");
    if (socket_fd != -1) {
        printf("free fd.\n");
        close(socket_fd);
        socket_fd = -1;
    }
    printf("OK\n");
    while (1);
    return 0;
}

遗留问题:
要注意的是,一个进程只能有一个闹钟时间,如果在调用alarm之前已设置过闹钟时间,则任何以前的闹钟时间都被新值所代替。

### 解决 Docker CE RPM 包下载时出现的 Curl SSL 连接错误问题 当在 CentOS 9 上安装 Docker CE 并遇到 `Curl error (28)` 或其他与 SSL 相关的连接错误时,可能的原因包括 OpenSSL 版本过旧、网络超时或 YUM 源配置不当等问题。以下是针对该问题的具体分析和解决方案。 #### 更新 OpenSSL 库 确保系统的 OpenSSL 库是最新的版本可以有效减少因加密协议不兼容而导致的连接失败问题。可以通过以下命令更新 OpenSSL: ```bash yum update -y openssl openssl-devel ``` 此操作会将系统中的 OpenSSL 和其开发库升级到最新稳定版[^1]。 #### 验证 YUM 源配置 如果仍然存在连接错误,则需检查当前使用的 YUM 源是否正确指向官方支持的镜像站点。对于 CentOS 9 用户来说,默认的 Docker 官方仓库可能不再完全适配新操作系统环境下的安全策略。建议替换为国内加速源(如阿里云),具体方法如下: 编辑 `/etc/yum.repos.d/docker-ce.repo` 文件并修改其中的内容为例: ```ini [docker-ce-stable] name=Docker CE Stable - $basearch baseurl=https://mirrors.aliyun.com/docker-ce/linux/centos/$releasever/$basearch/stable enabled=1 gpgcheck=1 gpgkey=https://download.docker.com/linux/centos/gpg ``` 完成更改之后再次运行安装指令测试效果如何改善[^5]。 #### 增加超时时间参数 有时即使解决了上述两个方面仍无法避免偶尔发生的短暂性网络波动引发的请求中断现象,在这种情况下可以在原有基础上增加额外选项延长等待周期来规避此类情况发生: ```bash export http_proxy=http://your-proxy-address:port https_proxy=http://your-proxy-address:port no_proxy=localhost,127.0.0.1 yum --timeout=300 install -y docker-ce docker-ce-cli containerd.io ``` 这里我们将默认超时期限设置成了五分钟(`--timeout=300`)以便给予更多缓冲空间给复杂环境下较慢响应速度的服务端节点处理我们的请求数据流传输过程[^2]。 另外值得注意的是某些特殊场景下还涉及到防火墙规则设定亦或是SELinux状态调整等因素也可能间接影响最终成果达成与否故而必要时候也应适当考虑这些潜在干扰变量的影响程度评估后再做相应处置措施决定[^3]. 最后关于利用docker-compose工具实现Nginx服务容器化部署过程中涉及SSL证书配置相关内容可参照专门文档资料进一步学习掌握实践技巧要点[^4].
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值