【问题标题】:how to work CORRECTLY with SSL_read() and select()?如何正确使用 SSL_read() 和 select()?
【发布时间】:2015-01-30 15:32:31
【问题描述】:

我尝试使用 OpenSSL 创建一个 C++ TLS 客户端,它在 Windows 上使用非阻塞套接字。

我想使用 SSL_read()/SSL_write() 和 select() 函数,但我找不到运行良好的算法,并且网络没有提供好的简单示例。在接收到最后一个数据块后,select() 已经返回超时。

OpenSSL api 看不懂,SSL_pending() 已经返回 0 并选择超时??

选择导致最后一个数据块的关键延迟。

recv_buffer() 的算法是这样的:

我有检查套接字是否可读或可写的函数(运行良好):

int CSocket::socket_RWable(int rw_flag, const int time_out)
{
    fd_set rwfs;
    int error = 0;
    struct timeval timeout;

    try
    {
        memset(&timeout, 0, sizeof(struct timeval));
        timeout.tv_sec = time_out;

        while( 1 ) // boucle de surveillance
        {
            FD_ZERO(&rwfs);
            FD_SET(m_socket, &rwfs);

            // surveiller la socket en lecture ou ecriture
            if(rw_flag == R_MODE)   
                error = select(m_socket+1, &rwfs, NULL, NULL, &timeout);
            else if(rw_flag == W_MODE) 
                error = select(m_socket+1, NULL, &rwfs, NULL, &timeout);

            if(error < 0) // echec de select
                throw 1;
            else if(error == 0) // fin du time out
                throw 2;

            // Une opération d' entree/sortie sur la socket est disponible
            if(FD_ISSET(m_socket, &rwfs) != 0)  
            {
                FD_CLR(m_socket, &rwfs );
                return 0;
            }
        }
    }
    catch(int ret)
    {
        FD_CLR(m_socket, &rwfs );
        if(ret == 1) throw CErreur("[-] CSocket : select : ", CWinUtil::Win_sys_error(NET_ERROR));
        else if(ret == 2)   return -1;
    }

    return -1;
}

更新:

这个函数将数据接收到缓冲区中,并在 las 数据块之后导致超时:

int CTLSClient::recv_buffer(char *buffer, const int buffer_size, const int  time_out)
{
    int selectErr = 0;
    int sslErr = 0;
    int retRead = 0;    
    int recvData = 0;   

    selectErr = m_socket->socket_RWable(R_MODE, time_out);

    while(selectErr == 0)
    {
        retRead = SSL_read(m_ssl, buffer+recvData, buffer_size-recvData);

        sslErr = SSL_get_error(m_ssl, retRead);

        if(sslErr == SSL_ERROR_NONE)
        {
            cout<<"DEBUG 2  SSL_ERROR_NONE recv data="<<retRead<<endl;
            recvData += retRead;
        }
        else if(sslErr == SSL_ERROR_WANT_READ)
        {
            cout<<"DEBUG 3  SSL_ERROR_WANT_READ select()"<<endl;
            selectErr = m_socket->socket_RWable(R_MODE, time_out);
        }
        else if(sslErr == SSL_ERROR_WANT_WRITE)
        {
            cout<<"DEBUG 4  SSL_ERROR_WANT_WRITE select()"<<endl;
            selectErr = m_socket->socket_RWable(W_MODE, time_out);
        }
        else if(sslErr == SSL_ERROR_ZERO_RETURN)
        {
            return -1;
        }
        else
            return -1;
    }

    return recvData;
}

这是连接到 POP3 服务器的输出:

DEBUG 2  SSL_ERROR_NONE recv data=35
DEBUG 3  SSL_ERROR_WANT_READ select()
[S]+OK BLU0-POP617 POP3 server ready
total data -> 35
DEBUG 2  SSL_ERROR_NONE recv data=23
DEBUG 3  SSL_ERROR_WANT_READ select()
[S]+OK password required
total data -> 23
DEBUG 2  SSL_ERROR_NONE recv data=30
DEBUG 3  SSL_ERROR_WANT_READ select()
[S]+OK mailbox has 180 messages
total data -> 30
DEBUG 2  SSL_ERROR_NONE recv data=18
DEBUG 3  SSL_ERROR_WANT_READ select()
[S]+OK 180 12374432
total data -> 18
DEBUG 2  SSL_ERROR_NONE recv data=13
DEBUG 3  SSL_ERROR_WANT_READ select()
[S]+OK 1 23899
total data -> 13
DEBUG 2  SSL_ERROR_NONE recv data=5
DEBUG 3  SSL_ERROR_WANT_READ select()
DEBUG 2  SSL_ERROR_NONE recv data=8192
DEBUG 2  SSL_ERROR_NONE recv data=8192
DEBUG 3  SSL_ERROR_WANT_READ select()
DEBUG 3  SSL_ERROR_WANT_READ select()
DEBUG 2  SSL_ERROR_NONE recv data=7521
DEBUG 3  SSL_ERROR_WANT_READ select()
[S]total data -> 23910

【问题讨论】:

    标签: windows sockets openssl nonblocking


    【解决方案1】:

    假设您已经阅读了标头,由于某种原因 SSL_read() 在阅读电子邮件消息后挂起并返回 SSL_WANT_READ。我通过一次一行地遍历消息正文直到找到结束时间段来解决了这个问题。当我到达这条线时,我调用 SSL_pending()。虽然没有待处理的数据,但它可以防止 SSL_read() 返回 SSL_WANT_READ 的无限循环。但是,我正在寻找更好的解决方案。

    for(;;)
    {
        char *line = ReadLine(ssl, buf, sizeof(buf));
        if(line != NULL)
        {
            if(*line == '.')
            {
                int pending = SSL_pending(ssl);
                if(pending > 0)
                {
                    int read = SSL_read(ssl,buf,pending);
                }
            }
        }
    }
    

    此函数一次读取一个字符,直到到达行尾字符并返回该行。

    char *ReadLine(SSL *ssl, char *buf, int size)
    {
        int i = 0;
        char *ptr = NULL;
        for (ptr = str; size > 1; size--, ptr++)
        {
            i = SSL_read(out, ptr, 1);
            switch (SSL_get_error(out, i)){
            case SSL_ERROR_NONE:
                break;
            case SSL_ERROR_ZERO_RETURN:
                break;
            case SSL_ERROR_WANT_READ:
                break;
            case SSL_ERROR_WANT_WRITE:
                break;
            default:
                TRACE("SSL problem\r\n");
            }
    
            if (*ptr == '\n')
                break;   
            if (*ptr == '\r'){
                ptr--;
            }
        }
    
    
        *ptr = '\0';
    
        return(str);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-01-10
      • 2018-09-10
      • 2020-12-05
      • 2015-12-19
      • 1970-01-01
      • 1970-01-01
      • 2019-03-26
      相关资源
      最近更新 更多