2012-09-03 22 views
5

Tôi đang viết một ứng dụng cần gửi tệp qua mạng. Tôi chỉ được dạy cách sử dụng các lớp java.net và java.io tiêu chuẩn cho đến nay (trong năm đầu tiên của đại học) vì vậy tôi không có kinh nghiệm với java.nio và netty và tất cả những điều tốt đẹp đó. Tôi đã có một máy chủ làm việc/khách hàng thiết lập bằng lớp Socket và ServerSocket cùng với BufferedInput/OutputStreams và BufferedFile suối, như sau:Làm cách nào để đệm luồng đầu vào/đầu ra/tệp Java của tôi đúng cách?

Máy chủ:

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!"); 
    } 
} 

Và khách hàng:

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!"); 
    } 
} 

Lúc đầu, tôi đã không sử dụng bộ đệm và viết từng int từ in.read(). Điều đó đã cho tôi về 200kb/s chuyển theo tiện ích màn hình mạng của tôi trên windows 7. Sau đó tôi thay đổi nó như trên nhưng đã sử dụng bộ đệm 4096 byte và có cùng tốc độ, nhưng tệp nhận được thường lớn hơn vài kilobyte so với tệp nguồn, và đó là vấn đề của tôi. Tôi đã thay đổi kích thước bộ đệm thành 8192 và bây giờ tôi nhận được khoảng 3,7-4,5mb/giây chuyển qua mạng không dây đến máy tính xách tay của mình, hiện tại đủ nhanh, nhưng tôi vẫn gặp vấn đề về tệp lớn hơn một chút (điều này sẽ gây ra để thất bại một thử nghiệm băm md5/sha) khi nó được nhận.

Vì vậy, câu hỏi của tôi là cách thích hợp để đệm để có tốc độ tốt và kết thúc với chính xác cùng một tệp ở phía bên kia? Bắt nó đi nhanh hơn một chút cũng sẽ tốt hơn nhưng tôi rất vui với tốc độ hiện tại. Tôi giả sử một bộ đệm lớn hơn là tốt hơn đến một điểm, tôi chỉ cần tìm ra điểm đó là gì.

Trả lời

5

Bạn đang bỏ qua kích thước dữ liệu thực sự đọc.

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

sẽ luôn ghi 8192 byte ngay cả khi chỉ đọc một byte. Thay vào đó tôi đề nghị sử dụng

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

bộ đệm của bạn có cùng kích thước như byte của bạn [] để họ không thực sự làm bất cứ điều gì vào lúc này.

MTU cho hầu hết các mạng là khoảng 1500 byte và bạn nhận được cải thiện hiệu suất trên các mạng chậm hơn (tối đa 1 GB) tối đa 2 KB. 8 KB cũng tốt. Lớn hơn điều đó không có khả năng giúp đỡ.

+0

Ah, đánh tôi với nó. – martijno

+0

@martijno Một dấu hiệu tôi cần phải làm chậm lại. ;) –

+0

Tôi hiểu việc bỏ qua phần kích thước dữ liệu gây ra sự cố nhưng tôi không hiểu điều đó đối với vòng lặp. Nó trông giống như nó đọc trước một cách và chỉ ghi dữ liệu từ bộ đệm đến điểm mà nội dung thực tế kết thúc - đúng không? EDIT: bạn có một quá nhiều dấu ngoặc, "((len = ..." nên được "(len = ..." – Logan