使用javax.net.ssl.SSLSocket

时间:2017-02-23 13:31:12

标签: java apache sockets ssl encryption

在我的主项目中,我在服务器和客户端之间传输大量二进制数据。

服务器是带有ArchLinux的odroid xu4。 客户端目前是MacbookPro。但是,在HP上使用Windows时也会出现此问题。

我花了一些时间才发现 javax.net.ssl加密导致通过TCP java套接字发送数据时性能大幅下降。

因此,我创建了一个测试环境 - 包括客户端,服务器和路由器 - 以及IntelliJ中用于测量性能统计数据的项目。

该测试仅测量将 5兆字节文件从客户端传输到服务器所需的时间。建立连接所需的时间 - SSL握手 - 大约需要2秒,这仍然没问题,也不是问题。但是,停止传输文件的时间会导致以下结果:

没有加密的套接字: 200ms

使用简单的 SCP - 命令: 200ms

使用 TLSv1 加密的套接字: 22秒

在下面我展示了我用于测试的客户端和服务器类:服务器

import javax.net.ssl.*;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;

public class Server {

    protected final static String keyPath = "/home/*****************/trust/keystore.keystore";

    public static void main(String[] args) throws Exception{
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
        KeyStore keyStore = KeyStore.getInstance("JKS");

        keyStore.load(new FileInputStream(keyPath), "*****************".toCharArray());
        keyManagerFactory.init(keyStore, "*****************".toCharArray());

        SSLContext context = SSLContext.getInstance("TLS");
        context.init(keyManagerFactory.getKeyManagers(), null, null);

        SSLServerSocketFactory factory = context.getServerSocketFactory();
        SSLServerSocket serverSocket = (SSLServerSocket) factory.createServerSocket(00000);

        serverSocket.setEnabledProtocols(new String[] {"SSLv3", "TLSv1"});

        SSLSocket socket = (SSLSocket) serverSocket.accept();
        socket.setTcpNoDelay(true);
        socket.setReceiveBufferSize(1024*1024*5);
        socket.setSendBufferSize(1024*1024*5);

        socket.addHandshakeCompletedListener(new HandshakeCompletedListener() {
            @Override
            public void handshakeCompleted(HandshakeCompletedEvent handshakeCompletedEvent) {
                System.out.println("CipherSuite: " + handshakeCompletedEvent.getCipherSuite());
                System.out.println("CipherSuite: " + handshakeCompletedEvent.getSession().getCipherSuite());
                System.out.println("Protocol: " + handshakeCompletedEvent.getSession().getProtocol());
            }
        });

        int size = 16*1024;
        byte[] buf = new byte[size];
        InputStream in = socket.getInputStream();

        int count;
        long sum = 0;
        long startTime = System.currentTimeMillis();
        while ((count = in.read(buf)) > -1) {
            sum += count;
        }
        System.out.println();
        long stopTime = System.currentTimeMillis();

        long elapsedTime = stopTime - startTime;
        System.out.println("Size:\t" + size + "\tTime:\t" + elapsedTime + "\tRead:\t" + sum/1024/1024);
    }
}

客户类:

import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;

public class Client {

    public static void main(String[] args) throws Exception{
        System.setProperty("javax.net.ssl.trustStore", Client.class.getResource("truststore.truststore").getPath());
        System.setProperty("javax.net.ssl.trustStorePassword", "*****************");

        SSLSocketFactory factory =  (SSLSocketFactory) SSLSocketFactory.getDefault();

        InetAddress address = InetAddress.getByName("192.168.***.***");
        //Socket socket = new Socket(address, 55552);
        SSLSocket socket = (SSLSocket) factory.createSocket(address, 55552);
        socket.setTcpNoDelay(true);
        socket.setReceiveBufferSize(1024*1024*5);
        socket.setSendBufferSize(1024*1024*5);

        for(String chipher : socket.getEnabledCipherSuites()) System.out.println(chipher);

        String path = "/Users/harald/Desktop/SpeedTest/file5M.img";
        File file = new File(path);

        int size = 16*1024;
        byte[] buf = new byte[size];
        InputStream in = new FileInputStream(file);

        int count;
        OutputStream out = socket.getOutputStream();
        long startTime = System.currentTimeMillis();
        while ((count = in.read(buf)) > -1) {
            out.write(buf, 0, count);
        }
        long stopTime = System.currentTimeMillis();
        long elapsedTime = stopTime - startTime;
        System.out.println("Time:\t" + elapsedTime + "\tRead:\t");
        out.close();
    }
}

可见我将SendBufferSize和ReceiveBufferSize设置为5MB。这给了我一个提示,发送和加密5MB文件所需的时间非常依赖于客户端和服务器的CPU。客户端需要 9秒来加密数据并将其放入套接字。 客户端具有 3,3 GHz Intel Core i7

odroid单板计算机当然有一个较弱的CPU,因此需要22秒才能接收和加密文件。 Linux命令 Top 向我显示传输文件时,一个核心始终 100%工作负载

结论是javax.net.ssl库中可能存在一些不好的实现问题,或者我的代码中遗忘了一些东西,导致了这个问题。

第一个解决方案可能是使用 org.apache.conn.ssl 而不是javax.net。 但是我目前认为SSLSockets只有一个客户端实现。如果还有机会创建SSLServerSocket,我将不再需要使用javax。但是,使用apache sslsockets并没有表现出来。

第二个解决方案可能是使用另一个密码套件,它具有较少的CPU工作负载。但我认为这还不够。

我真的希望有人可以帮我解决这个问题,或者知道如何编写错误报告,如果这是一个实现问题。

我期待着Harald。

0 个答案:

没有答案