有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

多线程Java 8:如何为SSLSocket执行超时。startHandshake()

我们有下面的线程来执行SSLHandshake,但在一些边缘情况下,我注意到((SSLSocket) clientSocket).startHandshake();被永久阻止,它不会进入下一个while循环代码块,其中SSL_握手_超时为1500毫秒,它工作正常,我想知道添加clientSocket.setSoTimeout(90000);是否会解决这个问题,或者应该以不同的方式处理

Main服务器握手阅读

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);
        }
    }
}

共 (2) 个答案

  1. # 1 楼答案

    总结一下评论(主要来自@user207421):是的,如果握手过程在“一段时间”后没有完成(但是不一定指定的超时1),那么通过^{}设置套接字超时将足以触发SocketTimeoutException(一个IOException的子类)

    这一点很简单,因为setSoTimeout()在套接字级别上工作,低于SSL握手:由^{}执行的握手协议涉及来自套接字Input/OutputStream的几次读/写,这将触发超时本身。换句话说:它本身不是“握手超时”,而是握手本身执行的所有读取操作的“读取超时”

    另外,请注意,您不需要自己调用startHandshake():当您第一次尝试从SSLSocket读取或写入数据时,JVM将自动执行此操作(不管怎样,在您从SSLServerSocket获得这样的套接字之后,您通常会提前执行此操作)


    1:由setSoTimeout(timeout)指定的超时用于单个^{}。因此,握手过程可以在执行的read()数乘以指定的timeout值之后超时(在最坏的情况下)

  2. # 2 楼答案

    SSLSocket的大多数实现都支持setHandshakeTimeout(),但它不是类定义的一部分。 我建议你看看Android代码的例子。这是如何应对的一个很好的例子: ssl socket factory wrapperssl socket factory

    检查他们如何尝试设置握手超时(如果存在):

        private void setHandshakeTimeout(SSLSocket sslSocket, int timeout) {
    
        try {
    
            // Most implementations of SSLSocket support setHandshakeTimeout(), but it is not
    
            // actually part of the class definition. We will attempt to set it using reflection.
    
            // If the particular implementation of SSLSocket we are using does not support this
    
            // function, then we will just have to use the default handshake timeout.
    
            sslSocket.getClass().getMethod("setHandshakeTimeout", int.class).invoke(sslSocket,
    
                    timeout);
    
        } catch (Exception e) {
    
            LogUtils.w(LogUtils.TAG, e, "unable to set handshake timeout");
    
        }
    
    }
    

    看看他们是如何建立联系的:

        SSLSocket sslsock = (SSLSocket)
    
            ((sock != null) ? sock : createSocket());
    
    
    
        if ((localAddress != null) || (localPort > 0)) {
    
    
    
            // we need to bind explicitly
    
            if (localPort < 0)
    
                localPort = 0; // indicates "any"
    
    
    
            InetSocketAddress isa =
    
                new InetSocketAddress(localAddress, localPort);
    
            sslsock.bind(isa);
    
        }
    
    
    
        int connTimeout = HttpConnectionParams.getConnectionTimeout(params);
    
        int soTimeout = HttpConnectionParams.getSoTimeout(params);
    
    
    
        InetSocketAddress remoteAddress;
    
        if (nameResolver != null) {
    
            remoteAddress = new InetSocketAddress(nameResolver.resolve(host), port);
    
        } else {
    
            remoteAddress = new InetSocketAddress(host, port);
    
        }
    
    
    
        sslsock.connect(remoteAddress, connTimeout);
    
    
    
        sslsock.setSoTimeout(soTimeout);
    
        try {
    
            hostnameVerifier.verify(host, sslsock);
    
            // verifyHostName() didn't blowup - good!
    
        } catch (IOException iox) {
    
            // close the socket before re-throwing the exception
    
            try { sslsock.close(); } catch (Exception x) { /*ignore*/ }
    
            throw iox;
    
        }
    
    
    
        return sslsock;
    

    希望有帮助