在java中将字节数组写入套接字输出流的最快方法是什么

时间:2014-05-15 18:43:24

标签: java sockets outputstream

作为标题,并假设字节数组的大小不大于16 KB。

目前我正在实施MySQL的中间件(如MySQL Proxy),这需要高吞吐量。但是从套接​​字读取数据并将数据写入套接字导致的开销。现在,我用

in = new DataInputStream(new BufferedInputStream(socket.getInputStream()))

out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()))

当读取数据并写入时,我使用

in.read(byte[] b)out.write(byte[] b, int offset, int len)out.flush()

有人能告诉我更好的方法吗?

4 个答案:

答案 0 :(得分:1)

如果你正在编写字节数组,它没有太大的区别。网络是限制因素,而不是API。我认为你已经接近最佳状态了。最重要的因素是内核中套接字发送缓冲区的大小,以及接收方的套接字接收缓冲区。

您可以调查NIO和直接缓冲区,但我怀疑您会看到显着差异。直接缓冲区实际上就是你只是在通道之间进行复制的情况,而NIO的其余部分实际上是关于可扩展性而不是单个通道的性能。

答案 1 :(得分:0)

由于您只是转发字节,因此可以通过不使用DataInputStream节省一点时间,而只使用BufferedInputStream.read()和BufferedOutputStream.write()。

答案 2 :(得分:0)

正如EJP所提到的,网络是限制因素。但这并没有阻止我试图在没有使用NIO的情况下实现我能想象到的最快的实现。问题是,当您写入另一个/ the same套接字时,您可以从套接字读取。一个线程无法执行此操作(读取或写入),因此需要多个线程。但是如果没有NIO,那就需要很多线程(大多数是在I / O上闲置等待)。 NIO有点复杂,但是当有很多低容量的连接时,它非常擅长使用很少的线程(参见Baldy提到的文章的this page摘要)。

无论如何,在非NIO测试类下面,您可以更新并使用它来查看(非)限制因素。

public class SocketForwarder {

public static void main(String[] args) {

    try {
        new SocketForwarder().forward();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public static final int portNumber = 54321;
public static final int maxSend = 1024 * 1024 * 100; // 100 MB
public static final int bufSize = 16 * 1024;
public static final int maxBufInMem = 128;

private static final SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss.SSS");

private final ExecutorService tp = Executors.newCachedThreadPool();
private final ArrayBlockingQueue<byte[]> bq = new ArrayBlockingQueue<byte[]>(maxBufInMem); 
private final CountDownLatch allReceived = new CountDownLatch(1);
private Socket from, to, sender, receiver;
private int bytesSend, bytesReceived;

public void forward() throws Exception {

    tp.execute(new Runnable() {
        public void run() {
            ServerSocket ss = null;
            try {
                ss = new ServerSocket(portNumber);
                from = ss.accept();
                to = ss.accept();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try { ss.close(); } catch (Exception ignored) {}
            }
        }
    });

    sender = new Socket(InetAddress.getLocalHost(), portNumber);
    receiver = new Socket(InetAddress.getLocalHost(), portNumber);

    // Setup proxy reader.
    tp.execute(new Runnable() {
        public void run() {
            byte[] buf = new byte[bufSize];
            try {
                InputStream in = from.getInputStream();
                int l = 0;
                while ((l = in.read(buf)) > 0) {
                    byte[] bufq = new byte[l];
                    System.arraycopy(buf, 0, bufq, 0, l);
                    bq.put(bufq);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
    // Setup proxy writer.
    tp.execute(new Runnable() {
        public void run() {
            try {
                OutputStream out = to.getOutputStream();
                while (true) {
                    byte[] bufq = bq.take();
                    out.write(bufq);
                    out.flush();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
    // Start receiver.
    tp.execute(new Runnable() {
        public void run() {
            byte[] buf = new byte[bufSize];
            try {
                InputStream in = receiver.getInputStream();
                int l = 0;
                while (bytesReceived < maxSend && (l = in.read(buf)) > 0) {
                    bytesReceived += l;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(df.format(new Date()) + " bytes received: " + bytesReceived);
            allReceived.countDown();
        }
    });
    // Start sender.
    tp.execute(new Runnable() {
        public void run() {
            Random random = new Random();
            try {
                OutputStream out = sender.getOutputStream();
                System.out.println(df.format(new Date())  + " start sending.");
                while (bytesSend < maxSend) {
                    byte[] buf = new byte[random.nextInt(bufSize)];
                    out.write(buf);
                    out.flush();
                    bytesSend += buf.length;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("Bytes send: " + bytesSend);
        }
    });
    try { 
        allReceived.await();
    } finally {
        close(sender);
        close(from);
        close(to);
        close(receiver);
        tp.shutdownNow();
    }
}

private static void close(Socket s) {
    try { s.close(); } catch (Exception ignored) {} 
}

}

我的计算机花了2秒钟在本地传输100MB,在涉及网络时期望更少。

答案 3 :(得分:-1)

为获得最佳吞吐量,您将需要使用NIO和ByteBuffers。 NIO将大部分工作保留在本机代码中读取和写入套接字,因此可以更快。

编写优秀的NIO代码要复杂得多,但根据您正在寻找的性能类型,可能值得付出努力。

有一些很好的NIO示例,还有一些很好的介绍和比较。我使用过的一种资源是http://tutorials.jenkov.com/java-nio/index.html