![](/img/trans.png)
[英]Java Socket OutputStream write one byte[] as two separate messages
[英]what is the fastest way to write a byte array to socket outputstream in java
作为标题,并假定字节数组的大小不大于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()
有人可以告诉我一种更好的方法吗?
如果您正在编写字节数组,则没有太大区别。 网络是限制因素,而不是API。 我认为您已经接近最佳状态了。 最重要的因素是内核中套接字发送缓冲区的大小,以及接收者处套接字接收缓冲区的大小。
您可以研究NIO和直接缓冲区,但是我怀疑您会看到很大的不同。 直接缓冲区确实适用于您仅在通道之间进行复制的情况,而NIO的其余部分实际上是关于可伸缩性而不是单个通道的性能。
由于您只是转发字节,因此不使用DataInputStream,而仅使用BufferedInputStream.read()和BufferedOutputStream.write()可以节省一些时间。
正如EJP所提到的,网络是限制因素。 但这并没有阻止我尝试不使用NIO进行我能想象的最快的实现。 问题是,您可以在写入另一个/ 同一套接字的同时从套接字读取。 一个线程无法做到这一点(读或写),因此需要多个线程。 但是如果没有NIO,则需要大量线程(尽管大多数情况下,它们处于空闲状态,等待I / O)。 NIO稍微复杂一些,但是当连接数量很多且数量很少时,NIO非常擅长使用很少的线程(请参阅Baldy提到的本文本页上的摘要)。
无论如何,您可以在非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,而在涉及网络时,期望值要低得多。
为了获得最佳吞吐量,您将要使用NIO和ByteBuffers。 NIO使大多数工作都以本机代码读取和写入套接字,因此可以更快。
编写好的NIO代码要复杂得多,但是根据您要寻找的性能类型,这值得您付出努力。
这里有一些不错的NIO示例,以及一些不错的介绍和比较。 我使用的一种资源是http://tutorials.jenkov.com/java-nio/index.html 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.