Как сделать передачу файлов более эффективной Java - PullRequest
6 голосов
/ 21 сентября 2011

У меня есть два беспроводных компьютера, подключенных к беспроводному маршрутизатору N.Каждый из этих компьютеров подключен со скоростью 108-150 Мбит / с.

Теоретически, я должен быть в состоянии передавать со скоростью 13,5 МБ / с до 18,75 МБ / с, при абсолютных лучших условиях.

Первый компьютер (который отправляет) использует оченьбыстрый SSD, который составляет около 100 МБ / с, если я правильно помню.Загрузка ЦП также остается ниже 20%.

Отправлено 1960273535 байт (1,8 ГБ) в 656367 мс.Это 2,8 МБ / с (22 из 108 мегабит).Когда я открываю диспетчер задач, я вижу, что используется только 25-27% сетевого подключения.

Я ищу любые идеи, предложения или улучшения, которые могут ускорить передачу (по сети).Я думал о буферизации файла с диска в потоке и отправке буферизованных данных из другого потока, но я не уверен, что это хорошая идея.Вот SSCCE:

Хост:

import java.io.*;
import java.net.*;


public class Host {


    public static void main(String[] args) throws IOException {


        ServerSocket servsock = new ServerSocket(15064);
        Socket sock = servsock.accept();
            long time = System.currentTimeMillis();

        OutputStream out = sock.getOutputStream();
        FileInputStream fileInputStream = new FileInputStream("C:\\complete.rar");

        byte [] buffer = new byte[64*1024]; 
        int bytesRead = 0;
        long totalSent = 0;

        while ( (bytesRead = fileInputStream.read(buffer)) != -1)
        {
            if (bytesRead > 0)
            {   
                out.write(buffer, 0, bytesRead);
                totalSent += bytesRead;
                System.out.println("sent " + totalSent);
            }   
        }

        sock.close();

        System.out.println("Sent " + totalSent + " bytes in "
                + (System.currentTimeMillis() - time) + "ms.");

    }
}

Клиент:

import java.io.*;
import java.net.*;

public class Client {

    public static void main(String[] args) throws Exception {
        Socket sock = new Socket("127.0.0.1", 15064);
        InputStream in = sock.getInputStream();
        FileOutputStream fileOutputStream = new FileOutputStream("output.rar");

        byte [] buffer = new byte[64*1024]; 
        int bytesRead = 0;

        while ( (bytesRead = in.read(buffer)) != -1)
            fileOutputStream.write(buffer, 0, bytesRead);
        sock.close();
        fileOutputStream.close();
    }
}

Редактировать: я попытался подключить сетевой диск и отправить файл через него, и Windowsсделал еще хуже - 2,35 МБ / с.В соответствии с этой статьей http://tinyurl.com/634qaqg отображение сетевого диска происходит быстрее, чем FTP, и у меня также нет времени продолжать играть и настраивать FTP-сервер.

Edit2: после изменениятаймер, оказывается, он передавал со скоростью 3 МБ / с через WiFi.Я ненавижу "теоретическую" пропускную способность.Когда я покупаю что-то, я хочу знать, что это НАСТОЯЩАЯ производительность.Оказывается, код действительно ограничен скоростью WiFi.Я все еще открыт для предложений.

Редактировать 3: После запуска программы в локальной сети 100 Мбит / с ей удалось передать файл со скоростью 11,8 МБ / с.Это очень хорошо, учитывая, что максимальная скорость передачи составляет 12,5 МБ / с.

Ответы [ 5 ]

6 голосов
/ 21 сентября 2011

При скорости 2,8 МБ / с маловероятно, что медлительность имеет какое-либо отношение к вашему коду. Это почти наверняка связано с тем, что беспроводная сеть не может достичь теоретической пропускной способности (возможно, из-за условий окружающей среды).

Нетрудно проверить, так ли это: просто время большой передачи файлов ftp или scp между теми же двумя компьютерами и посмотрите, какую пропускную способность вы видите.

5 голосов
/ 21 сентября 2011

Я предлагаю вам попробовать следующий код, который печатает

Wed Oct 26 14:21:03 BST 2011: Accepted a connection
Wed Oct 26 14:21:13 BST 2011: Transfer rate was 3212.5 MB/s

на сервере и на клиенте печатает

Wed Oct 26 14:21:03 BST 2011 Sending for 10.0 seconds.
Wed Oct 26 14:21:13 BST 2011 ... sent.
Wed Oct 26 14:21:13 BST 2011 ... received 33691287552
Send and received 3212.8 MB/s

Примечание: общая сумма передачи удваивается, так как все отправленоклиент на сервер отправляется с сервера на клиент.


import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Date;

public class EchoServerMain {
  public static void main(String... args) throws IOException {
    int port = args.length < 1 ? 55555 : Integer.parseInt(args[0]);
    ServerSocketChannel ss = ServerSocketChannel.open();
    ss.socket().bind(new InetSocketAddress(port));
    while (!ss.socket().isClosed()) {
      SocketChannel s = ss.accept();
      System.out.println(new Date() + ": Accepted a connection");
      long start = System.nanoTime();
      ByteBuffer bytes = ByteBuffer.allocateDirect(32*1024);
      int len;
      long total = 0;
      // Thank you @EJP, for a more elegant single loop.
      while ((len = s.read(bytes)) >= 0 || bytes.position() > 0) { 
        bytes.flip(); 
        s.write(bytes); 
        bytes.compact(); 
        total += len;
      }
      long time = System.nanoTime() - start;
      System.out.printf(new Date() + ": Transfer rate was %.1f MB/s%n", total * 1e9 / 1024 / 1024 / time);
    }
    ss.close();
  }
}

и

import java.io.EOFException;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Date;

public class EchoClientMain {
  public static void main(String ... args) throws IOException {
    String hostname = args.length < 1 ? "localhost" : args[0];
    int port = args.length < 2 ? 55555 : Integer.parseInt(args[1]);
    double seconds = args.length < 3 ? 10 : Double.parseDouble(args[2]);

    SocketChannel s = SocketChannel.open(new InetSocketAddress(hostname, port));
    s.configureBlocking(false);
    ByteBuffer bytes = ByteBuffer.allocateDirect(32*1024);

    System.out.printf(new Date()+ " Sending for %.1f seconds.%n", seconds);
    long start = System.nanoTime();
    long dataSent = 0, dataReceived = 0;
    // run for 10 seconds.
    while(start + seconds*1e9 > System.nanoTime()) {
      bytes.clear();
      int wlen = s.write(bytes);
      if (wlen < 0) throw new IOException();
      dataSent += wlen;

      bytes.clear();
      int rlen = s.read(bytes);
      if (rlen < 0) throw new EOFException();
      dataReceived += rlen;
    }
    System.out.println(new Date()+ " ... sent.");

    while(dataReceived < dataSent) {
      bytes.clear();
      int rlen = s.read(bytes);
      if (rlen < 0) throw new EOFException();
      dataReceived += rlen;
    }
    s.close();
    long time = System.nanoTime() - start;
    System.out.println(new Date()+ " ... received "+dataReceived);
    System.out.printf("Send and received %.1f MB/s%n", dataReceived * 1e9/1024/1024/time);
  }
}
0 голосов
/ 22 сентября 2011

Просто установите очень большой буфер отправки сокетов и, если возможно, установите очень большой буфер приема сокетов в приемнике.«Оптимизация» кода в основном не способствует этим сценариям.

0 голосов
/ 21 сентября 2011

Протестируйте ту же программу, используя проводную связь Fast Ethernet (100 Мбит / с) между компьютерами (а затем, возможно, используя канал 1 Гбит)Таким образом, вы увидите, действительно ли скорость передачи ограничена вашей программой или ссылкой.

0 голосов
/ 21 сентября 2011

Ваш таймер неверен!запускать его следует после того, как вы примете соединение, а не при запуске хоста

Вы пытались увеличить размер буфера?

...