通过套接字复制文件的最快方法

时间:2016-11-22 13:55:26

标签: java sockets

哪种方法是通过套接字复制文件的最快方法?我尝试了几种方法,但我不相信我找到了有关传输和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
 }

有什么建议吗?

2 个答案:

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

}