MySQL SSLException: 关闭入站之前未收到同行的close_notify

MySQL SSLException: 关闭入站之前未收到同行的close_notify

在本文中,我们将介绍MySQL SSLException中的一个常见问题,即关闭入站之前未收到同行的close_notify错误。MySQL在与对等方建立SSL连接时,会使用TLS(Transport Layer Security)协议。在TLS握手过程中,存在关闭通道的部分,其中每个对等方都应该向对方发送close_notify。这将被用于关闭会话并执行释放操作。

之所以会出现“关闭入站之前未收到同行的close_notify”错误,是因为MySQL服务器(TLS客户端)已经开始关闭入站通道,而对等方服务器(TLS服务器)仍在继续发送数据。这将导致无法完全清除当前的TLS会话,从而产生错误。

以下是如何解决此问题:

阅读更多:MySQL 教程

修改MySQL连接参数

为了解决这个问题,可以在MySQL连接字符串中添加两个参数:autoReconnect和useSSL。其中autoReconnect参数将MySQL连接设置为自动重新连接,并useSSL设置为启用SSL加密。这样会引发一些其他问题,但对于海量读取场景可能是一种权衡方案。

String url = "jdbc:mysql://localhost:3306/mydatabase?autoReconnect=true&useSSL=true";
String user = "root";
String password = "root";

try {
  Connection connection = DriverManager.getConnection(url, user, password);
} catch (SQLException e) {
  e.printStackTrace();
}

使用TLS协议

如果您正在使用自己基于TLS的协议或需要自定义TLS握手过程,那么您可以考虑使用Java的SSLSocket类,而不是MySQL提供的JDBC驱动程序。

SSLSocket的创建顺序为套接字(socket),然后是SSL套接字(试图在该套接字上建立安全连接),然后是通信线程。接下来是要注意的几点:

  • 您必须手动向对方发送close_notify以关闭会话(在TLS握手期间完成的正常操作)。
  • 您还必须等待对等方发送close_notify。在接收对等方的close_notify之前,不应关闭套接字。这个等待可能会导致您的程序挂起,这是一个需要注意的点。
  • 您应该注意不要在write()操作的返回值等于-1时关闭套接字,因为这意味着它不能正确关闭。

以下是一个使用SSLSocket的示例:

// Key pair, trust store, and algorithm are all required to establish the SSL connection.
KeyStore keyStore = null, trustStore = null;
String keyStoreAlias = "", trustStoreAlias = "";
String password = "";

// Reference to an established SSLSocket.
SSLSocket sslSocket = null;

// Connection establishment code.
try {
    KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(ALGORITHM); // set algorithm used
    TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(ALGORITHM); // set algorithm used

    keyStore = KeyStore.getInstance(KEY_STORE_TYPE); // set type of keystore used
    trustStore = KeyStore.getInstance(TRUST_STORE_TYPE); // set type of truststore used

    keyStore.load(KEY_STORE_INPUT_STREAM, password.toCharArray()); // load keystore
    trustStore.load(TrustStoreInputStream, password.toCharArray()); // load truststore

    keyManagerFactory.init(keyStore, password.toCharArray()); // initialize key manager with keystore 
    trustManagerFactory.init(trustStore); // initialize trust manager with truststore

    SSLContext sslContext = SSLContext.getInstance(TLS_PROTOCOL); // set TLS protocol used
    sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null); // initialize SSL context with key manager and trust manager
    sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket(targetIp, targetPort); // create socket with connect to given target

    // SSL/TLS handshake.
    sslSocket.startHandshake(); // start handshake
    // You might need to wait for the flush() to finish.
    OutputStream outputStream = sslSocket.getOutputStream(); // establish output stream

    // Close tunnel.
    outputStream.write(("close_notify" + CLOSE_NOTIFY_EOL).getBytes(); // send close_notify
    outputStream.flush(); // flush output stream

    InputStream inputStream = sslSocket.getInputStream(); // establish input stream
    int byteAvailable = inputStream.available();
    while (byteAvailable != 0) {
        inputStream.skip(byteAvailable); // skip all data
        byteAvailable = inputStream.available(); // check for available data
    }

    sslSocket.close(); // close SSL socket
} catch (Exception e) {
    e.printStackTrace();
}

关闭Nagle算法

Nagle算法是由John Nagle在1984年发明的,作为在TCP的若干实现中一个解决小白点(microscopic inefficiency)的方法。虽然它在许多情况下非常有效,但在某些情况下有时会出现问题。关闭它可能会帮助解决上述问题,但这是不推荐的做法,因为存在一些风险。

要关闭Nagle算法,可以使用TCP_NODELAY套接字选项:

boolean nodelay = true; // enable No-delay algorithm on this socket
Socket socket = . . . ;
socket.setTcpNoDelay(nodelay);

总结

MySQL SSLException: 关闭入站之前未收到同行的close_notify错误是一个常见的问题,但可以通过设置MySQL连接参数、使用TLS协议或关闭Nagle算法来解决。但我们要注意,关闭Nagle算法有一些潜在的风险。总之,在设置MySQL的SSL连接时,我们需要仔细考虑每一个细节,以确保其正常运行。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程