#java #serversocket #objectinputstream
#java #serversocket #objectinputstream
Вопрос:
Я пытался отправить непрерывные изображения с веб-камеры (используя Webcam-api) от клиента через ObjectOutStream на сервер и показать его на ярлыке кадра и нашел код на youtube, но через некоторое время программа выдает ошибку «из памяти». Кто-нибудь может объяснить ее причину? Может быть, InputStream на стороне сервера хранит все изображения, которые не требуются. Если это так, предложите какие-либо методы очистки InputStream на сервере.
TestClient :
import java.io.IOException;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.*;
import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import com.github.sarxos.webcam.Webcam;
import com.github.sarxos.webcam.WebcamResolution;
public class TestClient {
static Socket socket;
public static void main(String[] args) throws IOException,ClassNotFoundException{
Webcam webcam = Webcam.getDefault();
webcam.setViewSize(WebcamResolution.VGA.getSize());
System.out.println(WebcamResolution.VGA.getSize());
webcam.open();
socket = new Socket("localhost",5001);
ObjectOutputStream dout = new ObjectOutputStream(socket.getOutputStream());
JLabel label = new JLabel();
JFrame frame = new JFrame("Client");
frame.setSize(WebcamResolution.VGA.getSize());
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
label.setSize(WebcamResolution.VGA.getSize());
label.setVisible(true);
frame.add(label);
frame.setVisible(true);
while(true) {
BufferedImage wc = webcam.getImage();
ImageIcon ic = new ImageIcon(wc);
label.setIcon(ic);
dout.writeObject(ic);
dout.flush();
}
}
}
Тестовый сервер:
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.ServerSocket;
import java.net.Socket;
import javax.swing.*;
public class TestServer {
public static void main(String[] args) throws IOException,ClassNotFoundException{
ServerSocket server = new ServerSocket(5001);
System.out.println("Waiting...");
Socket socket = server.accept();
System.out.println("Connected ..");
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
JLabel label = new JLabel();
JFrame frame = new JFrame("ServerSide");
frame.setSize(640,480);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
label.setSize(640,480);
label.setVisible(true);
frame.add(label);
frame.setVisible(true);
while(true) {
label.setIcon((ImageIcon)in.readObject());
}
}
}
Ошибка через некоторое время::
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.base/java.lang.reflect.Array.newArray(Native Method)
at java.base/java.lang.reflect.Array.newInstance(Array.java:78)
at java.base/java.io.ObjectInputStream.readArray(ObjectInputStream.java:2036)
at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1656)
at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:482)
at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:440)
at java.desktop/javax.swing.ImageIcon.readObject(ImageIcon.java:501)
at java.base/jdk.internal.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at java.base/java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1175)
at java.base/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2295)
at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2166)
at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1668)
at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:482)
at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:440)
at TestServer.main(TestServer.java:29)
Редактировать::
Я пробовал это, но сервер получает изображение только в первый раз, после чего возникает исключение нулевой точки. Я не могу понять, почему?
Клиент:
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while(true) {
BufferedImage wc = webcam.getImage();
ImageIO.write(wc, "jpg", bos);
byte imgBytes[] = bos.toByteArray();
out.write(imgBytes,0,imgBytes.length);
bos.flush();
}
Сервер ::
DataInputStream in=new DataInputStream(socket.getInputStream());
ImageInputStream imgin = ImageIO.createImageInputStream(in);
while(true){
BufferedImage img=ImageIO.read(imgin);
ImageIcon ic = new ImageIcon(img);
label.setIcon(ic);
}
Комментарии:
1. Попробуйте добавить a
Thread.sleep
в любой из циклов (либо на клиенте, либо на сервере) и посмотрите, что произойдет
Ответ №1:
Ни InputStream, ни ObjectInputStream не могут быть «очищены», и они не «сохраняют» какие-либо предыдущие результаты. Скорее всего, биты на проводе повреждены, и отправляемые данные заставляют операцию чтения пытаться выделить гигантский массив «случайно».
Обратите внимание, что отправка ImageIcons через ObjectOutputStream невероятно неэффективна; протокол тратит тонны пропускной способности и тратит много циклов процессора, пытаясь сериализовать этот объект, который не был предназначен для этого. Лучше сохранить ваше изображение в какой-нибудь быстрый формат изображения (возможно, не PNG) и отправить его, что попутно также значительно упростит отладку поврежденных отправлений.
Комментарии:
1. Это не может быть правдой:
Most likely the bits on the wire are corrupted, and the data being sent is causing the read operation to try to allocate a gigantic array 'by accident'.
как могут быть повреждены биты на проводе? Если бы это было так, Интернет никогда бы не работал. Существуют определенные проверки, начиная с сериализации / десериализации java и заканчивая протоколом TCP и так далее. Почти на всех уровнях есть некоторые проверки, чтобы предотвратить это.2. Проблемы с версиями между классами (serialVersionUID затем переопределяет проверку этого), например.
3. Но проблемы с версиями приводят к исключениям во время выполнения, они не приводят к ошибкам нехватки памяти, не так ли?
4. Они делают. Например, если существуют разные версии объекта, но установлен sVUID (обычная практика), происходит смещение поля.
Ответ №2:
То, что вы здесь делаете, постоянно устанавливает значок метки:
while(true) {
label.setIcon((ImageIcon)in.readObject());
}
То, что делает этот код, создает новые экземпляры ImageIcon
, ссылку label
на этот экземпляр, а затем, когда приходит новое изображение, ссылка на исходный объект теряется, и сборщик мусора не может очистить его или не получает возможности очистить его, поскольку это происходит в цикле без каких-либо пауз.
Таким образом, если камера непрерывно генерирует 60 кадров в секунду, у сборщика мусора не будет возможности очистить память, поскольку (гипотетически) в памяти будет создаваться 60 объектов в секунду.
Вы можете отслеживать свое приложение с помощью jvisualvm и видеть, что это так.
Комментарии:
1. Ничто из этого не соответствует действительности. in.readObject() заканчивает замораживанием этого потока (поскольку, в конечном итоге, вы попадаете в сетевой сокет, который блокируется), и в этот момент сборщик мусора сработает просто отлично. Даже если поток никогда не блокируется, он будет GC просто отлично; как только вы запросите у виртуальной машины выделение, которое она не может вам предоставить, оно (обычно, это сложно) сначала GC.
2. Я не говорю, что это на 100% верно, но это моя теория. Он не остановится в сокете, если есть объекты, доступные для чтения.
3. Ваша теория основана на неправильных представлениях о том, как работает виртуальная машина. Это не может быть правдой.
4. Я попытался добавить system.gc(); в каждом цикле он все равно зависал.
5. .. это потому, что этот ответ не имеет смысла, @AAYUSHSHANDILYA