哪种方法是通过套接字复制文件的最快方法?我尝试了几种方法,但我不相信我找到了有关传输和CPU使用的最快方法。 (最佳结果:175mBit / s(SSD / GBit网络))
服务器:
ByteBuffer bb = ByteBuffer.allocate(packet_size);
DataOutputStream data_out = new DataOutputStream(socket.getOutputStream());
while(working){
int count =0;
int packet_size = in.readInt();
long pos = in.readLong();
if(filechannel.position()!=requested_pos){
filechannel.position(requested_pos);
}
bb.limit(packet_size);
bb.position(0);
if((count=filechannel.read(bb))>0){ //FileInputStream.getChannel()
data_out.writeInt(count);
data_out.write(bb.array(),0,count);
}else{
working=false;
}
}
客户端:
for(long i=0;i<=steps;i++){
data_out.writeInt(packet_size); //requested packet size
data_out.writeLong(i*packet_size); //requested file position
count=in.readInt();
bb.clear();
bb.limit(count);
lastRead=0;
while(lastRead<count){
lastRead+=in.read(bytes,lastRead,count-lastRead);
}
bb.put(bytes,0,count);
bb.position(0);
filechannel.write(bb); // filechannel over RandomAccessFile
}
有什么建议吗?
答案 0 :(得分:3)
您只关注问题的一半。用于发送/接收的代码只是一个因素。无论你如何努力优化它,如果你用不合适的参数设置套接字,性能就会受到很大影响。
对于大型数据传输,请确保套接字具有相当大的缓冲区。我选择至少64kb,可能更大。发送和接收缓冲区可以单独设置,对于发送器,您需要大(r)发送缓冲区,而接收器可以设置大(r)接收缓冲区。
socket.setReceiveBufferSize(int);
socket.setSendBufferSize(int);
socket.setTcpNoDelay(false);
将TCP NO DELAY设置为OFF,除非您知道您正在做什么以及确认确实需要之后。它永远不会提高吞吐量,相反,它可能会牺牲吞吐量,有利于减少延迟。
接下来就是定制你的发送者代码,以便始终保持缓冲区完整。为了从文件中读取最大速度,写入套接字应该分成两个独立的线程,使用某种队列相互通信。队列中的块应该相当大(至少几kb)。
同样,接收代码应尽力使接收缓冲区尽可能为空。同样,为了获得最大速度,这需要两个线程,一个读取套接字,另一个处理数据。像发件人一样在队列之间排队。
队列的工作是在从实际网络传输中读取文件/写入文件的数据时解除停顿,反之亦然。
以上是无论传输通道如何都能获得最大吞吐量的通用模式。较慢的通道将保持完全饱和,无论是文件读/写还是网络传输。
可以调整缓冲区大小以挤出可能性能的最后几个百分点(我开始使用64kb的套接字和队列中的8kb块,最大队列大小为1mb,这应该可以提供合理的性能尽可能最大化。
您可能遇到的另一个限制因素是TCP传输窗口缩放(特别是在高带宽,高延迟连接上)。除了确保接收器尽可能快地清空接收缓冲区之外,您无法从Java端执行任何操作。操作系统级别存在调整选项。
答案 1 :(得分:0)
您想使用NIO。
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
public class FileServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try(final InputStream is = new BufferedInputStream((InputStream) <YOUR INPUT STREAM TO A FILE HERE>);
final OutputStream os = new BufferedOutputStream(response.getOutputStream()); ) {
fastCopy(is, os);
}
}
public static void fastCopy(final InputStream src, final OutputStream dest) throws IOException {
fastCopy(Channels.newChannel(src), Channels.newChannel(dest));
}
public static void fastCopy(final ReadableByteChannel src, final WritableByteChannel dest) throws IOException {
final ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
while(src.read(buffer) != -1) {
buffer.flip();
dest.write(buffer);
buffer.compact();
}
buffer.flip();
while(buffer.hasRemaining()) {
dest.write(buffer);
}
}
}
}