OpenSSL非阻塞套接字SSL_Read()仍然阻塞

时间:2013-05-05 05:15:23

标签: c++ sockets ssl openssl winsock

我有一个用C ++编写的用于Windows的客户端/服务器程序,我几乎将OpenSSL集成到了。

我确实想说明我查看了与我自己类似的所有其他帖子,我还没有找到问题的正确答案。大多数其他exmaples非常接近答案,但我似乎无法产生实际结果。

对于我的测试用例,我只是每隔5秒使用OpenSSL安全套接字从客户端向服务器传递一个7个字符的字符串。我没有发送任何其他信息。最初的问题是我没有正确使用SSL_read而且我没有阅读所需的明显额外数据,并且SSL_read将停止响应并且客户端将失去连接。

在阅读了所有其他示例后,我进一步完成了这一步,但我无法获得理想的结果。我尝试了超过10种类型的SSL_read实现,并且在读取数据时我无法使代码无阻塞。我将发布我最新的实现(不是非常优化):

int received = -1;
int tE = 0;

    if( tE != SSL_ERROR_WANT_READ && tE != SSL_ERROR_WANT_WRITE )
    {
        received = SSL_read(sSecureSocket.ssl, buffer, sizeof(buffer)); 
        if( received == -1 )
        {
            tE = SSL_get_error( sSecureSocket.ssl, received );
            if( tE == SSL_ERROR_WANT_READ )
            {
                int tReadWaiting = 1;
                while( tReadWaiting )
                {
                    tReadWaiting = 0;
                    fd_set rfd;
                    FD_ZERO(&rfd);
                    FD_SET(receiveSocket, &rfd);
                    timeval tv = { 0 };
                    select(receiveSocket+1, &rfd, 0, 0, &tv);
                    if (!FD_ISSET(receiveSocket, &rfd))
                    {
                        tReadWaiting = 1;
                    }
                }
                received = SSL_read(sSecureSocket.ssl, buffer, sizeof(buffer)); 
            }
        }
    }

此代码在每次发送时都会成功提取数据包,但正如您所看到的,一旦触发SSL_ERROR_WANT_READ / WRITE,我必须使用select()语句。这种方法取自本论坛的另一个主题,比如你应该读取UNTIL,调用SSL_ERROR_WANT_READ / WRITER。另外,我确实使用SSL_pending()但它总是在我拥有的每种情况下返回0,从来不是非零数字。

该程序使用非阻塞套接字编写,并对Windows消息进行WSAASyncSelect调用。所有这一切都很好。

但是,所发生的事情与我的预期相反。代码似乎总是等待不存在的数据。我意识到SSL套接字也意味着在随机时间进行握手。我只想弄清楚为什么一旦我收到来自服务器的数据,为什么看起来SSL_read似乎有数据,或者select()检测数据需要在我发送的5秒间隔之间读取我的数据包,以便select()循环在整个时间内阻塞。即使数据包被完全读取,下一条消息也会被回调到读取函数中,程序本身永远不会让此函数执行任何其他过程。

非常感谢任何帮助,我以前从未接受过我的智慧结束编程,这让我非常接近它。

更新1:

这是套接字监听代码:

ctx = sSecureSocket.InitServerCTX();        // initialize SSL 
    sSecureSocket.LoadCertificates(ctx, VGlobal::CURRWORKDIR + "\\openssl2\\" + "mycert.pem", VGlobal::CURRWORKDIR + "\\openssl2\\" + "mycert.pem"); // load certs 
    servSecureSocket = sSecureSocket.OpenListener(39245);    // create server socket 

在服务器上接受套接字时调用:

//Always connect as a secure socket first
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
SSL *ssl;

receiveSocket = accept(servSecureSocket, (struct sockaddr*)&addr, &len);  // accept connection as usual 
printf("Connection: %s:%d\n",inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
sSecureSocket.ssl = SSL_new(ctx);              // get new SSL state with context 
SSL_set_fd(sSecureSocket.ssl, receiveSocket);      // set connection socket to SSL state 
sSecureSocket.Servlet(sSecureSocket.ssl);         // service connection 

这些是功能:

//-----------------------------------------------------
int SecureSocket::OpenListener(int port)
{   int sd;
struct sockaddr_in addr;

WSADATA wsadata;
int error = WSAStartup( 0x0202, &wsadata );

if( error )
    return false;

sd = socket(PF_INET, SOCK_STREAM, 0);
//bzero(&addr, sizeof(addr));
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
if ( bind(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 )
{
    perror("can't bind port");
   // abort();
}
if ( listen(sd, 10) != 0 )
{
    perror("Can't configure listening port");
   // abort();
}
return sd;
}

//-----------------------------------------------------
SSL_CTX* SecureSocket::InitServerCTX(void)
{   
SSL_METHOD *method;
SSL_CTX *ctx;

OpenSSL_add_all_algorithms();  /* load & register all cryptos, etc. */
SSL_load_error_strings();   /* load all error messages */
ctx = SSL_CTX_new(SSLv23_server_method());
if ( ctx == NULL )
{
    ERR_print_errors_fp(stderr);
}
return ctx;
}

//-----------------------------------------------------
void SecureSocket::LoadCertificates(SSL_CTX* ctx, CString CertFile, CString KeyFile)
{
/* set the local certificate from CertFile */
if ( SSL_CTX_use_certificate_file(ctx, CertFile, SSL_FILETYPE_PEM) <= 0 )
{
    ERR_print_errors_fp(stderr);
}
/* set the private key from KeyFile (may be the same as CertFile) */
if ( SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0 )
{
    ERR_print_errors_fp(stderr);
}
/* verify private key */
if ( !SSL_CTX_check_private_key(ctx) )
{
    fprintf(stderr, "Private key does not match the public certificate\n");
}
}

//-----------------------------------------------------
void SecureSocket::ShowCerts(SSL* ssl)
{   X509 *cert;
char *line;

cert = SSL_get_peer_certificate(ssl); /* Get certificates (if available) */
if ( cert != NULL )
{
    printf("Server certificates:\n");
    line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
    printf("Subject: %s\n", line);
    free(line);
    line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
    printf("Issuer: %s\n", line);
    free(line);
    X509_free(cert);
}
else
    printf("No certificates.\n");
}

//-----------------------------------------------------
void SecureSocket::Servlet(SSL* ssl) /* Serve the connection -- threadable */
{
int acceptDone = SSL_accept(ssl);

//If this blocking this will be done immediately, if it is not we must loop until it is complete
while( acceptDone < 1 )
{
    acceptDone = SSL_accept(ssl);
}

ShowCerts(ssl);        /* get any certificates */
}

0 个答案:

没有答案