feifeiiong edited a comment on issue #1096: URL: https://github.com/apache/incubator-brpc/issues/1096#issuecomment-619385096
感谢您的回复!第四点确实是如您所说。关于1,3点,有一些不同的看法: 1. DoRead 的设计确实是读到0即代表约定遇到EOF,但是SSL_read为0时的处理并不完备,以下内容摘自man: The following return values can occur: >0 The read operation was successful; the return value is the number of bytes actually read from the TLS/SSL connection. 0 The read operation was not successful. The reason may either be a clean shutdown due to a "close notify" alert sent by the peer (in which case the SSL_RECEIVED_SHUTDOWN flag in the ssl shutdown state is set (see ssl_shutdown(3), ssl_set_shutdown(3)). It is also possible, that the peer simply shut down the underlying transport and the shutdown is incomplete. Call SSL_get_error() with the return value ret to find out, whether an error occurred or the connection was shut down cleanly ( SSL_ERROR_ZERO_RETURN SSL_get_error: SSL_ERROR_ZERO_RETURN The TLS/SSL connection has been closed. SSL_ERROR_SYSCALL Some I/O error occurred. The OpenSSL error queue may contain more information on the error. If the error queue is empty (i.e. ERR_get_error() returns 0), ret can be used to find out more about the error: If ret == 0, an EOF was observed that violates the protocol. If ret == -1, the underlying BIO reported an I/O error (for socket I/O on Unix systems, consult errno for details). 在iobuf中是这样处理的: ``` const int rc = SSL_read(ssl, _block->data + _block->size, read_len); *ssl_error = SSL_get_error(ssl, rc); if (rc > 0) { const IOBuf::BlockRef r = { (uint32_t)_block->size, (uint32_t)rc, _block }; _push_back_ref(r); _block->size += rc; if (_block->full()) { Block* const saved_next = _block->portal_next; _block->dec_ref(); // _block may be deleted _block = saved_next; } nr += rc; } else { if (rc < 0) { if (*ssl_error == SSL_ERROR_WANT_READ || (*ssl_error == SSL_ERROR_SYSCALL && BIO_fd_non_fatal_error(errno) == 1)) { // Non fatal error, tell caller to read again *ssl_error = SSL_ERROR_WANT_READ; } else { // Other errors are fatal return rc; } } return (nr > 0 ? nr : rc); ``` 在rc == 0时,返回,并置错误码,此处的错误码,有可能是SSL_ERROR_SYSCALL/SSL_ERROR_ZERO_RETURN, 在socket.cpp: int ssl_error = 0; ssize_t nr = _read_buf.append_from_SSL_channel(_ssl_session, &ssl_error, size_hint); switch (ssl_error) { case SSL_ERROR_NONE: // `nr' > 0 break; case SSL_ERROR_WANT_READ: // Regard this error as EAGAIN errno = EAGAIN; break; case SSL_ERROR_WANT_WRITE: // Disable renegotiation errno = EPROTO; return -1; default: { const unsigned long e = ERR_get_error(); if (nr == 0) { // Socket EOF or SSL session EOF } else if (e != 0) { LOG(WARNING) << "Fail to read from ssl_fd=" << fd() << ": " << SSLError(e); errno = ESSL; } else { // System error with corresponding errno set PLOG(WARNING) << "Fail to read from ssl_fd=" << fd(); } break; } 如上,以上错误码被default捕获,而此刻nr 有可能!=0 (iobuf.cpp : return (nr > 0 ? nr : rc);), 从而导致该EOF被忽略,导致socket无法关闭。在实际应用中,该问题已经频繁被触发,打印日志类似: Fail to read from ssl_fd=id:Success DoWrite有类似的问题,这里就不贴代码了,烦请您看看! ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@brpc.apache.org For additional commands, e-mail: dev-h...@brpc.apache.org