如何正确地缓冲Java输入/输出/文件流?

6
我正在编写一个应用程序,需要通过网络发送文件。到目前为止(我在大学的第一年),我只学习了如何使用标准的java.net和java.io类库,因此我没有接触过java.nio、netty和所有这些好的东西。 我已经设置了一个工作的服务器/客户端,并使用Socket和ServerSocket类以及BufferedInput/OutputStreams和BufferedFile stream来实现,如下所示:
服务器:
    public class FiletestServer {

    static ServerSocket server;
    static BufferedInputStream in;
    static BufferedOutputStream out;

    public static void main(String[] args) throws Exception {
    server = new ServerSocket(12354);
    System.out.println("Waiting for client...");
    Socket s = server.accept();
    in = new BufferedInputStream(s.getInputStream(), 8192);
    out = new BufferedOutputStream(s.getOutputStream(), 8192);
    File f = new File("test.avi");
    BufferedInputStream fin = new BufferedInputStream(new FileInputStream(f), 8192);

    System.out.println("Sending to client...");
    byte[] b = new byte[8192];
    while (fin.read(b) != -1) {
        out.write(b);
    }
    fin.close();
    out.close();
    in.close();
    s.close();
    server.close();
    System.out.println("done!");
    }
}

客户端:

public class FiletestClient {

    public static void main(String[] args) throws Exception {
    System.out.println("Connecting to server...");
    Socket s;
    if (args.length < 1) {
        s = new Socket("", 12354);
    } else {
        s = new Socket(args[0], 12354);
    }
    System.out.println("Connected.");
    BufferedInputStream in = new BufferedInputStream(s.getInputStream(), 8192);
    BufferedOutputStream out = new BufferedOutputStream(s.getOutputStream(), 8192);
    File f = new File("test.avi");
    System.out.println("Receiving...");
    FileOutputStream fout = new FileOutputStream(f);
    byte[] b = new byte[8192];
    while (in.read(b) != -1) {
        fout.write(b);
    }
    fout.close();
    in.close();
    out.close();
    s.close();
    System.out.println("Done!");
    }
}

起初我没有使用缓冲区,而是从in.read()中写入每个int。根据我的网络监视器小工具,在Windows 7上传输速度约为200kb/s。然后我更改了上述方法,但使用了4096字节的缓冲区,得到了相同的速度,但接收到的文件通常比源文件大几千字节,这就是我的问题所在。我将缓冲区大小更改为8192,现在通过无线传输到我的笔记本电脑的传输速度约为3.7-4.5mb/sec,对我来说足够快了,但我仍然有一个问题,即接收到的文件会略微变大(这会导致它未能通过md5/sha哈希测试)。因此,我的问题是使用缓冲区的适当方式是什么,以获得良好的速度并最终在另一端得到完全相同的文件?让它跑得更快也很好,但目前的速度已经可以了。我认为更大的缓冲区更好,但要找到这个点。
2个回答

5

您忽略了实际读取数据的大小。

while (in.read(b) != -1) {
    fout.write(b);
}

即使只读取一个字节,也会始终写入8192字节。相反,我建议使用

for(int len; ((len = in.read(b)) > 0;)
   fout.write(b, 0, len);

你的缓冲区和byte[]数组大小相同,所以目前它们并没有起到任何作用。
大多数网络的MTU约为1500字节,在较慢的网络上(高达1 GB)可以获得最多2 KB的性能提升。8 KB也可以。超过这个大小不太可能有帮助。

@martijno 这是我需要放慢脚步的信号。 ;) - Peter Lawrey
我理解忽略数据大小部分会导致问题,但是我不理解那个for循环。它看起来有点像提前读取数据,并且仅写入缓冲区中实际内容结束的位置 - 这正确吗?编辑:你有一个多余的括号,"((len = ..." 应该是 "(len = ..."。 - ldam
@biltong 它会将读取的数据量存储在 len 中,并只输出相应数量的数据。 - Peter Lawrey
太好了,我的文件在另一端通过了MD5测试,并且通过千兆网络传输到我的笔记本电脑时速度达到了约30MB/秒。太棒了!非常感谢! - ldam

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接