我们有下面的线程可以执行SSLHandshake,但是在某些情况下,我注意到((SSLSocket) clientSocket).startHandshake();被永久阻止,并且不会转到下一个while循环代码块,其中SSL_HANDSHAKE_TIMEOUT是1500毫秒,并且可以正常工作,我想知道是否添加clientSocket.setSoTimeout(90000);是否可以解决此问题还是应该以其他方式处理?

MainServerHandshakeThread

public class MainServerHandshakeThread implements com.ssltunnel.utilities.threading.Shutdown, Runnable {
    private final Socket clientSocket;
    private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(MainServerHandshakeThread.class.getName());
    private boolean done;

    public MainServerHandshakeThread(Socket clientSocket) {
        this.clientSocket = clientSocket;
    }

    private void handshake() throws CertificateExpiredException, InterruptedException, IOException {

        long start = System.currentTimeMillis();

        ((SSLSocket) clientSocket).setNeedClientAuth(true);
        MainServerHandshakeHandler handshake = new MainServerHandshakeHandler();
        ((SSLSocket) clientSocket).addHandshakeCompletedListener(handshake);
        ((SSLSocket) clientSocket).startHandshake();


        while (!handshake.isDone() && !done) {
            Thread.sleep(10);
            long duration = System.currentTimeMillis() - start;
            if (duration>SSL_HANDSHAKE_TIMEOUT) {
                done = true;
                LOG.warn("Handshake timeout");
            }
        }
        long stop = System.currentTimeMillis();
        serialNumber = handshake.getSerialNumber();
        LOG.info("MainServer Handshake Handshake done in ms: " + ((stop - start))+" For serialNumber "+serialNumber );

    }

    @Override
    public void run() {
        try {
            handshake();
        } catch (CertificateExpiredException ex) {
            LOG.error("Client Certificate Expired", ex.getMessage());
            SocketUtils.closeQuietly(clientSocket);
        }
        catch (InterruptedException ex) {
            LOG.error("Interrupted waiting for handshake", ex);
            SocketUtils.closeQuietly(clientSocket);
        }
        catch (IOException ex) {
            LOG.error("IO Error waiting for handshake", ex);
            SocketUtils.closeQuietly(clientSocket);
        }
        finally {
            LOG.debug("Handshake thread is done");
            done = true;
        }

    }

    @Override
    public void shutdown() {
        if (clientSocket!=null) {
            SocketUtils.closeQuietly(clientSocket);
        }
    }
}

最佳答案

总结评论(大部分来自@ user207421):是的,如果握手过程在“一段时间”之后没有完成(但不一定是通过),则通过 socket.setSoTimeout(timeout) 设置套接字超时就足以触发SocketTimeoutException(IOException的子类)。指定的超时时间1)。

可以简单地解释一下原因,因为setSoTimeout()在SSL握手之下的套接字级别工作: startHandshake() 执行的握手协议(protocol)涉及对套接字Input/OutputStream的多次读取/写入,这将触发超时本身。换句话说:它本身不是“握手超时”,而是握手本身执行的所有读取操作的“读取超时”。

另外,请注意,您无需自己调用startHandshake():当您首次尝试从SSLSocket读取或写入时,JVM会自动执行此操作(通常无论如何从SSLServerSocket获得这样的套接字后,通常都会这样做)。

1:setSoTimeout(timeout)指定的超时是针对单个 read() 的。因此,握手过程可能会在执行的read()数乘以您指定的timeout值后超时(在最坏的情况下)。

10-05 22:11