2017-11-14 11 views
-1

OpenSSL 연결에서 블로킹 소켓을 사용하고 있습니다. SSL_read는 때때로 몇 초 동안 차단됩니다. 서버에서 - BIO_write는 가변 버퍼 크기로 데이터를 전송하는 데 사용됩니다. 클라이언트 - 먼저 버퍼 크기를 얻는 SSL_read가 성공하지만 데이터가 성공적으로 전송 되었더라도 SSL_read를 따라 몇 초 동안 버퍼 데이터 블록을 가져옵니다 (이 문제는 2 ~ 3 분 후 시뮬레이션 됨). 나는 클라이언트 읽기 기능을 호출하기 위해 poll()을 기다린다. 블로킹 소켓에서 이러한 문제를 해결하는 방법은 무엇입니까?OpenSSL 차단 소켓 SSL_read 블록

서버 코드

void process_and_send() { 
    // sending variable size buffer each time 
    // sbuf - first 4 bytes contains sbuf size information 
    send_data(sbuf, sbufSize); 
} 


void send_data(void *sbuf, int pending_len) { 
     while(pending_len > 0) { 
       result = BIO_write(bio, sbuf, pending_len); 
       if(result == 0) { 
         attempts = 0; 
         LOG_D("%s", log_str(SSL_CONN_CLOSE)); 
         SSL_FN_TRACE("connection closed\n"); 
         break; 
       } 
       else if(result < 0) { 
         LOG_I("%s", log_str(SSL_WRITE_FAIL)); 
         SSL_FN_TRACE("BIO_write fail\n"); 
         if(errno == EINTR) { 
           continue; 
         } 
         if(errno == EAGAIN) { 
           attempts++; 
           continue; 
         } 
         if(errno == EWOULDBLOCK) { 
           attempts++; 
           continue; 
         } 
         break; 
       } 
       else { 
         BIO_flush(bio); 
         pending_len -= result; 
         sbuf += result; 
       } 
     } 
} 

클라이언트 코드

// wait on poll() and call receive_and_process 
void receive_and_process() { 
    int rbufSize = 0; 
    // get the size of data to read 
    receive_data((void *)&rbufSize, sizeof(Int)); 
    // this call blocks for few seconds 
    receive_data(rbuf, rbufSize); 
} 

void receive_data(void *rbuf, int pending_len) { 
    while(pending_len > 0) { 
        result = SSL_read(ssl, rbuf, pending_len); 
      if(result == 0) { 
        LOG_D("%s", log_str(SSL_CONN_CLOSE)); 
        SSL_FN_TRACE("connection closed\n"); 
        return NULL; 
      } 
      else if(result < 0) { 
        if(errno == ETIMEDOUT) { 
          SSL_FN_ERROR("SSL read timeout: \n"); 
          continue; 
        } 
        if(errno == EINTR) { 
          continue; 
        } 
        if(errno == EAGAIN) { 
          continue; 
        } 
        if(errno == EWOULDBLOCK) { 
          continue; 
        } 

        SSL_FN_ERROR("SSL read fail error no: %s\n", 
            ERR_reason_error_string(ERR_get_error())); 
        LOG_I("%s", log_str(SSL_READ_FAIL)); 
        return NULL; 
      } 
      pending_len -= result; 
      rbuf += result; 
      FN_ERROR("after read full data pending len %d\n", pending_len); 
    } 
} 
+0

. 블로킹 소켓 블록을 통한 I/O. 너의 질문? – EJP

+0

왜 차단합니까? 데이터가 성공적으로 전송 되었더라도 고마워요. 레미. –

답변

2

음과 같이 receive_data()void 반환 형식을 가지고 있기 때문에, 우선, 클라이언트 코드가 너무 return NULL 컴파일러는, 컴파일 할 수 없습니다 오류. 또한 void* 포인터에서 += 연산자를 사용할 수 없으며 이는 컴파일러 오류이기도합니다. 그 옆에

, SSL_read() 경우 반환 < 0, 당신은 실패 이유를 알아 대신 errnoSSL_get_error()을 사용해야합니다. SSL_get_error()SSL_ERROR_SYSCALL을 반환하지 않는 한 errno을 사용하지 마십시오. SSL_get_error()SSL_ERROR_SSL 인 경우 ERR_get_error() 및 관련 함수를 대신 사용하십시오. SSL_ERROR_WANT_READSSL_ERROR_WANT_WRITE 오류를 처리하고 있는지 확인하십시오.

또한 멀티 바이트 정수를 전송할 때 기계 경계를 넘어 전송하는 경우 엔디안 문제를 처리해야합니다. htonl()ntohl()과 같은 함수를 사용하여 네트워크 바이트 순서로 연결을 통해 정수를 전송하는 것이 가장 좋습니다.

이와 비슷한 더 많은 것을보십시오 :

서버 :

void process_and_send() { 
    // sending variable size buffer each time 
    // sbuf - DO NOT store the size information in the first 4 bytes! 
    //  handle the size separately... 
    int32_t size = htonl(sbufSize); 
    if (send_data(&size, sizeof(size))) 
     send_data(sbuf, sbufSize); 
} 

bool send_data(void *sbuf, int pending_len) { 
    unsigned char *pbuf = (unsigned char *) sbuf; 
    while (pending_len > 0) { 
     result = BIO_write(bio, pbuf, pending_len); 
     if (result > 0) { 
      BIO_flush(bio); 
      pbuf += result; 
      pending_len -= result; 
     } 
     else if (result == 0) { 
      attempts = 0; 
      LOG_D("%s", log_str(SSL_CONN_CLOSE)); 
      SSL_FN_TRACE("connection closed\n"); 
      return false; 
     } 
     else if (!BIO_should_retry(bio)) { 
      LOG_I("%s", log_str(SSL_WRITE_FAIL)); 
      SSL_FN_TRACE("BIO_write fail\n"); 
      return false; 
     } 
     else { 
      ++attempts; 
     } 
    } 
    return true; 
} 

클라이언트 : 동의

// wait on poll() and call receive_and_process 
void receive_and_process() { 
    int32_t rbufSize = 0; 
    // get the size of data to read 
    if (receive_data(&rbufSize, sizeof(rbufSize))) { 
     rbufSize = ntohl(rbufSize); 
     // TODO: make sure rbuf is at least rbufSize in size... 
     receive_data(rbuf, rbufSize); 
    } 
} 

bool receive_data(void *rbuf, int pending_len) { 
    unsigned char *pbuf = (unsigned char *) rbuf; 
    while (pending_len > 0) { 
     result = SSL_read(ssl, pbuf, pending_len); 
     if (result > 0) { 
      pbuf += result; 
      pending_len -= result; 
      FN_ERROR("after read full data pending len %d\n", pending_len); 
     } 
     else { 
      result = SSL_get_error(); 
      if (result == SSL_ERROR_ZERO_RETURN) { 
       LOG_D("%s", log_str(SSL_CONN_CLOSE)); 
       SSL_FN_TRACE("connection closed\n"); 
      } 
      else { 
       if (result == SSL_ERROR_WANT_READ) { 
        // TODO: use select() to wait for the socket to be readable before trying again... 
        continue; 
       } 
       else if (result == SSL_ERROR_WANT_WRITE) { 
        // TODO: use select() to wait for the socket to be writable before trying again... 
        continue; 
       } 
       else if (result == SSL_ERROR_SYSCALL) { 
        if ((errno == EINTR) || (errno == EAGAIN) || (errno == EWOULDBLOCK)) { 
         continue; 
        } 

        if (errno == ETIMEDOUT) { 
         SSL_FN_ERROR("SSL read timeout: \n"); 
         continue; 
        } 

        SSL_FN_ERROR("SSL read fail error no: %d\n", errno); 
       } 
       else if (result == SSL_ERROR_SSL) { 
        SSL_FN_ERROR("SSL read fail error no: %s\n", 
         ERR_reason_error_string(ERR_get_error())); 
       } 
       else { 
        SSL_FN_ERROR("SSL read fail error no: %d\n", result); 
       } 
       LOG_I("%s", log_str(SSL_READ_FAIL)); 
      } 
      return false; 
     } 
    } 
    return true; 
} 
+0

. 내가 말한대로 코드에서 변경되었습니다. 그러나 SSL_read는 데이터가 성공적으로 전송 되더라도 2000 밀리 초 이상 차단되는 경우가 있습니다. –

+0

@ArunrajShanmugam 그러면 올바른 형식으로 데이터를 읽지 못할 가능성이 있습니다. 데이터 크기를 잘못 읽은 경우와 같이 실제 전송 된 것보다 많은 바이트를 요청하면 블로킹 읽기가 차단됩니다. 읽기 논리를 다시 확인하십시오. –

+0

좋아요. 디버깅을하고 서버가 여러 클라이언트에서 데이터를 수신 할 때이 문제가 자주 시뮬레이트됩니다. –