SSL_read及SSL_write支持超时

原始的socket编程中 readwrite 支持超时是很容易实现的,如使用 select 或者 setsockopt 设置读写超时并在 readwrite 出错后根据 errno 判断是否为超时引起。

但是在 SSL 编程中对底层socket调用 select 以及使用 errno 行为是未定义的。

使用 setsockopt 在底层的socket上设置读写后, SSL_readSSL_write 出错会返回ssl错误码 SSL_ERROR_WANT_READSSL_ERROR_WANT_WRITE , 但是被信号中断或者底层SSL需要重新握手也会导致 SSL_readSSL_write 返回同样的ssl错误码。

如果能够将信号屏蔽掉,并启用SSL自动重新握手,就能够实现 SSL_readSSL_write 超时检测。

  • 屏蔽信号

    忽略应用产生的信号,如:

    signal(SIGPIPE, SIG_IGN);
    signal(SIGCHLD, SIG_IGN);
    
  • 在底层socket上设置超时
    struct timeval tv;
    tv.tv_sec  = 10;
    tv.tv_usec = 0;
    setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char*)&tv, sizeof(struct timeval));
    setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(struct timeval));
    
  • 启用自动重新握手
    SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
    
  • SSL_readSSL_write 判断是否超时出错
    int readed = SSL_read(ssl, data, size);
    if (readed <= 0) {
        if (SSL_get_error(ssl, readed) == SSL_ERROR_WANT_READ) {
            // timeout
        } else {
            // error
        }
    }
    
    int writed = SSL_write(ssl, data, size);
    if (writed <= 0) {
        if (SSL_get_error(ssl, writed) == SSL_ERROR_WANT_WRITE) {
            // timeout
        } else {
            // error
        }
    }