2013-07-15 1 views
0

Недавно я написал простую клиентскую программу для передачи файлов по стандартным сокетам TCP. Средняя пропускная способность составляла около 2,2 Мбит/с по каналу WiFi. Мой вопрос: Возможно ли передать большой файл (например, 5 ГБ) по нескольким потокам ввода-вывода данных, чтобы каждый поток мог передавать несколько частей одного и того же файла параллельно (для этой цели можно использовать разные потоки)? Эти части файла могут быть повторно собраны на принимающей стороне. Я попытался разбить небольшой файл и передать его через dataoutputstream. Первый сегмент работает отлично, но я не знаю, как выборочно читать поток ввода файла (я также опробовал методы меток() и reset() для выборочного чтения, но не использовал)Передача файлов через java-сокеты с использованием нескольких потоков ввода-вывода

Вот мой код (для целей тестирования, я перенаправил вывод FileOutputStream):

public static void main(String[] args) { 
    // TODO Auto-generated method stub 
    final File myFile=new File("/home/evinish/Documents/Android/testPicture.jpg"); 
    long N=myFile.length(); 
    try { 
     FileInputStream in=new FileInputStream(myFile); 
     FileOutputStream f0=new FileOutputStream("/home/evinish/Documents/Android/File1.jpg"); 
     FileOutputStream f1=new FileOutputStream("/home/evinish/Documents/Android/File2.jpg"); 
     FileOutputStream f2=new FileOutputStream("/home/evinish/Documents/Android/File3.jpg"); 

     byte[] buffer=new byte[4096]; 
     int i=1, noofbytes; 
     long acc=0; 
     while(acc<=(N/3)) { 
      noofbytes=in.read(buffer, 0, 4096); 
      f0.write(buffer, 0, noofbytes); 
      acc=i*noofbytes; 
      i++; 
     } 
     f0.close(); 

я получил первый сегмент моего файла (это может быть скопировано в DataOutputStream в одном потоке). Может ли кто-нибудь предложить, как читать оставшуюся часть файла (после N/3-байта) в сегменте N/3, чтобы три потока могли использоваться в трех потоках для одновременной работы?

Вот код, чтобы объединить сегменты файла в приемнике конца:

package com.mergefilespackage; 
import java.io.BufferedInputStream; 
import java.io.BufferedOutputStream; 
import java.io.Closeable; 
import java.io.File; 
import java.io.FileInputStream; 
import java.io.FileNotFoundException; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.OutputStream; 

public class MergeFiles { 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) throws Exception{ 
     // TODO Auto-generated method stub 
     IOCopier.joinFiles(new File("/home/evinish/Documents/Android/File1.jpg"), new File[] { 
      new File("/home/evinish/Documents/Android/File2.jpg"), new File("/home/evinish/Documents/Android/File3.jpg")}); 
    } 
} 
class IOCopier { 
    public static void joinFiles(File destination, File[] sources) 
      throws IOException { 
     OutputStream output = null; 
     try { 
      output = createAppendableStream(destination); 
      for (File source : sources) { 
       appendFile(output, source); 
      } 
     } finally { 
      IOUtils.closeQuietly(output); 
     } 
    } 

    private static BufferedOutputStream createAppendableStream(File destination) 
      throws FileNotFoundException { 
     return new BufferedOutputStream(new FileOutputStream(destination, true)); 
    } 

    private static void appendFile(OutputStream output, File source) 
      throws IOException { 
     InputStream input = null; 
     try { 
      input = new BufferedInputStream(new FileInputStream(source)); 
      IOUtils.copy(input, output); 
     } finally { 
      IOUtils.closeQuietly(input); 
     } 
    } 
} 
class IOUtils { 
    private static final int BUFFER_SIZE = 1024 * 4; 

    public static long copy(InputStream input, OutputStream output) 
      throws IOException { 
     byte[] buffer = new byte[BUFFER_SIZE]; 
     long count = 0; 
     int n = 0; 
     while (-1 != (n = input.read(buffer))) { 
      output.write(buffer, 0, n); 
      count += n; 
     } 
     return count; 
    } 

    public static void closeQuietly(Closeable output) { 
     try { 
      if (output != null) { 
       output.close(); 
      } 
     } catch (IOException ioe) { 
      ioe.printStackTrace(); 
     } 
    } 
} 

Любая помощь будет высоко ценится! Заранее спасибо!

+1

Если вы пытаетесь перекачать больше данных по одному и тому же каналу WiFi, это, скорее всего, будет медленнее, поскольку каждый сокет увеличит конкуренцию для других. – hexafraction

+0

+1 к @hexafraction. Вопрос в том, что является ограничивающим фактором. Я подозреваю (например, @hex), что вы ограничены скоростью Wi-Fi, и поскольку другие соединения будут на одном и том же канале Wi-Fi, они не смогут работать параллельно. – Gray

+0

@Gray Добавлено как ответ. – hexafraction

ответ

3

Вы не можете получить больше скорости по той же ссылке с большим количеством сокетов. Каждый сокет отправляет определенное количество пакетов, каждый из которых имеет определенный размер. Поскольку мы удваиваем количество сокетов, количество пакетов/сек * сокетов уменьшается вдвое, а затем уменьшалось еще больше из-за столкновений, накладных расходов и конкуренции. Пакеты начинают падать, беспорядочно и паниковать. ОС не может справиться с pandemonium потерянных ACK, и WiFi-карта изо всех сил пытается передать с такой скоростью. Он теряет свои низкоуровневые качества. По мере того, как пакеты теряются, отчаянный стек TCP набирает скорость передачи. Если бы это было возможно подняться из-за улучшения сигнала, то теперь он застрял на более низкой скорости из-за синдрома глупого окна или другой формы тупика TCP.

Любая попытка WiFi получить более высокие скорости из более широких полос несущей, MiMo или нескольких путей уже реализована как выигрыш, даже с одним разъемом. Вы не можете двигаться дальше.

Теперь, подождите. Мы совсем ниже скорости WiFi, не так ли? Конечно, нам нужно использовать буферизацию!

Убедитесь, что вы создали объекты BufferedWriter и BufferedReader из методов getInputStream или getOutputStream вашего сокета. Затем пишите в/read из этих буферов. Ваша скорость может несколько возрасти.

+0

+1 Пропускная способность для каждого сокета уменьшается вдвое из-за пределов пропускной способности канала WIFI. – Gray

0

Вы можете получить массив байтов FileInputStream и разбить его каждые 10 КБ (каждые 10.000 байт). Затем отправьте эти части через потоки по порядку.

На сервере вы можете снова собрать массивы и прочитать файл из этого массива гигантских байтов.

+1

10KB **! = ** 10000bytes – BackSlash

+0

Это не приведет к более высокой пропускной способности, более сложному коду. – Gray

+0

Ну ... Затем разделите он каждые 10240 байт – MinecraftShamrock