#java #sockets
#java #сокеты
Вопрос:
Мне было интересно, могу ли я получить некоторые рекомендации по решению моей проблемы…
Видите ли, я пытаюсь освоить программирование сокетов; Мне удалось создать клиент и сервер; сервер пишет клиенту без проблем; мне даже удается отправлять файлы с помощью
byte [] mybytearray = new byte [(int)myFile.length()];
FileInputStream fis = new FileInputStream(myFile);
Это на стороне сервера.
На стороне клиента
byte [] mybytearray = new byte [filesize];
InputStream is = sock.getInputStream();
FileOutputStream fileos = new FileOutputStream("Filename.dat");
BufferedOutputStream bufferos = new BufferedOutputStream(fileos);
bytesRead = is.read(mybytearray,0,mybytearray.length);
current = bytesRead;
do {
bytesRead =
is.read(mybytearray, current, (mybytearray.length-current));
if(bytesRead >= 0) current = bytesRead;
} while(bytesRead > -1);
bufferos.write(mybytearray, 0 , current);
bufferos.flush();
long end = System.currentTimeMillis();
System.out.println(end-start);
bufferos.close();
Мой вопрос заключается в том, что я не могу отправлять большие файлы; Я продолжаю получать
«Исключение в потоке «main» java.lang.Ошибка OutOfMemoryError: пространство кучи Java »
Любые мысли или указания о том, как я могу управлять отправкой больших файлов клиенту с сервера? Я имею в виду размеры, скажем, 600 МБ или около того …. любая мысль? Высоко ценится … спасибо
Ответ №1:
Что ж, в настоящее время вашей куче приходится пытаться удерживать ‘mybytearray’, который соответствует размеру файла, который вы пытаетесь получить (предположительно). Вам нужно переместить bos.запишите операцию () в цикл и сделайте ‘mybytearray’ фиксированным размером. Вот пример копирования одного потока в другой, который на самом деле не заботится о размере передаваемых данных:
public static void stream(InputStream in, OutputStream out)
throws IOException {
byte[] buf = new byte[1024];
int bytesRead = 0;
try {
while (-1 != (bytesRead = in.read(buf, 0, buf.length))) {
out.write(buf, 0, bytesRead);
}
} catch (IOException e) {
log.error("Error with streaming op: " e.getMessage());
throw (e);
} finally {
try{
in.close();
out.flush();
out.close();
} catch (Exception e){}//Ignore
}
}
Комментарии:
1. Если возникает исключение IOException, то оба InputStream и OutputStream остаются открытыми. Вероятно, лучше закрыть эти (проглатывающие исключения) в блоке finally.
2. PS Вероятно, есть сторонние библиотеки, которые справляются с этим, но я обычно реализую служебный метод: IOUtil.closeIgnoringExceptions(закрываемый… closeables), чтобы выполнить этот шаг и избежать необходимости второй вложенной попытки … наконец, в первом блоке finally. Служебный метод должен обрабатывать нулевые ссылки, чтобы избежать случаев, когда один из потоков создавал исключение во время создания.
3. сработало как шарм; Я применил решение на основе вашего примера и сработал как шарм … спасибо вам всем…
Ответ №2:
У вас заканчивается память, потому что вы выделяете mybytearray
ее на длину filesize
. Одним из возможных решений является вызов JVM с большей кучей; например -Xmx1G
.
Однако еще один момент заключается в том, нужно ли вам читать весь файл в память — на стороне сервера это определенно не так: вы можете просто читать файл по частям в массив фиксированного размера, а затем записывать эти фрагменты в выходной поток. Таким образом, объем памяти вашего сервера не увеличится, когда он попытается отправить большие файлы обратно клиенту.
Комментарии:
1. Да — абсолютно просто прочитайте фрагменты разумного размера и отправьте их. Кусков размером 512 КБ было бы более чем достаточно. Даже если вы увеличили размер кучи, если отправляемый вами файл приближается к размеру кучи (но не превышает его), но вы хотите, чтобы этот сервер мог обслуживать несколько одновременных подключений, тогда, как только вы откроете второй большой файл, вы столкнетесь ста же проблема.
2. Увеличение размера кучи — ужасное решение этой проблемы
3. @normalocity: Согласен; моя точка зрения относительно размера кучи была больше для клиентской части, где клиент пытается прочитать файл с сервера в массив.
4. @Mick: Вы полностью проигнорировали второй абзац?
5. @Adamski — Да, понял — я полностью поддерживаю ваш ответ. Моя точка зрения была больше в пользу ibininja
Ответ №3:
Не считывайте полный файл в ОЗУ для отправки, а отправляйте файл по частям.
byte [] mybytearray = new byte [(int)myFile.length()];
плохо, если (int)myFile.length()
больше, чем умещается в памяти.
Вместо этого используйте
byte [] buffer= new byte[4096];
или так и сделайте цикл, считывая из файла и отправляя в сокет.
Комментарии:
1. Спасибо; в настоящее время изучаю, как отправлять файл по частям
Ответ №4:
Это означает, что JVM исчерпала память.. Таким образом, вы должны в основном увеличить пространство кучи JVM.. Поэтому я думаю, что проблема не в отправке больших файлов..
Комментарии:
1. -1 потому что есть лучшие способы сделать это. У него всегда будет заканчиваться память, если он отправляет файл больше, чем место в куче с его текущим решением.
2. да, вы правы, решения, которые предлагают другие, намного лучше.. Разделение больших файлов на массив памяти фиксированного размера имеет больше смысла 🙂