2016-04-11 3 views
0

То, что я пытаюсь достичь:

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

Для простоты я буду подражать камеру захвата кадров из сохраненного видеофайла и отправка эти кадры один за другим через сокеты для всех подключенных клиентов (Да, камера может обрабатывать более одного клиента). На стороне клиента я получаю кадры, а затем я буду отображать их в jPanel один за другим, чтобы создать эффект воспроизведения видео.

Я уже сделал все это, но он работает только на пару кадров, после чего он внезапно останавливается без исключения.


Сервер-Side:


Это основная функция в камеры класса:

public static void main(String[] args) throws InterruptedException, IOException, RemoteException, AlreadyBoundException { 

    ServerSocket ssock = new ServerSocket(1234); 
    System.out.println("Listening"); 
    Camera.getInstance().startCamera(); // Starts reading the frames from the video file 

    while (true) { 

     Socket sock = ssock.accept(); 
     System.out.println("Connected"); 

     ClientConnection con = new ClientConnection(sock); // Creates a new connection 

     // Runs the connection on it's own thread 
     Thread conThread = new Thread(con); 
     conThread.start(); 

     // Keeps a reference to the connection so it can be used later to send frames 
     Camera.getInstance().connections.add(con); 

    } 

} 



Отрывки из ClientConnection класса:

Конструктор:

public ClientConnection(Socket csocket) throws IOException { 
    this.csocket = csocket; 
    outStream = new PrintStream(csocket.getOutputStream()); 
    objectOutStream = new ObjectOutputStream(csocket.getOutputStream()); 
} 



ClientConnection класс реализует интерфейс работоспособный, поэтому он может работать в отдельном потоке. Метод run будет отвечать за получение предопределенных сообщений (например, «SET_MOVIE») от клиента и выполнить соответствующие действия. Эти действия и то, что они делают, не имеют отношения к вопросу, поэтому мы можем смело игнорировать их. Вот метод запуска:

@Override 
public void run() { 
    try { 
     inStream = new Scanner(csocket.getInputStream()); 
     String msg; 
     while (inStream.hasNext()) { 
      msg = inStream.nextLine(); 

      if (msg.equals("SET_MOVIE")) { 
       setMovie(); 
      } else if (msg.equals("SET_IDLE")) { 
       setIdle(); 
      } else if (msg.equals("FORCE_STATE_ON")) { 
       forceStateOn(); 
      } else if (msg.equals("FORCE_STATE_OFF")) { 
       forceStateOff(); 
      } else if (msg.equals("DISCONNECT")) { 
       // TO-DO 
      } 

     } 
    } catch (IOException ex) { 
     Logger.getLogger(ClientConnection.class.getName()).log(Level.SEVERE, null, ex); 
    } 
} 



Это метод sendFrame в ClientConnection класса. Он вызывается каждый раз, когда новый фрейм доступен и готов к отправке.

// SEND_FRAME here works as an indicator to the client so that it can expect 
// the image and start reading it 
public void sendFrame(Frame _frame) throws IOException { 
    outStream.println("SEND_FRAME"); //tells the client there is a new frame 
    outStream.println(_frame.getCaptureTime()); //sends the time in which the frame was captured 
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 
    ImageIO.write(_frame.getFrame(), "jpg", byteArrayOutputStream); 
    byte[] size = ByteBuffer.allocate(4).putInt(byteArrayOutputStream.size()).array(); 
    outStream.write(size); 
    outStream.write(byteArrayOutputStream.toByteArray()); 
    outStream.flush(); 
} 



Клиент-Side:


Это основной метод, он просто создает новый CameraConnection и запустить его на своем собственном потоке.

public static void main(String[] args) throws InterruptedException, IOException { 
    Thread client = new Thread(new CameraConnection("Cam_1", 1234)); 
    client.start(); 
} 



Это CameraConnection конструктор:

public CameraConnection(String name, int port) throws IOException { 

    this.name = name; 
    clientSocket = new Socket("localhost", port); 

    // This scanner will be used to read messages sent from the server 
    // such as "SEND_FRAME" 
    inStream_scanner = new Scanner(clientSocket.getInputStream()); 

    // This inputStream will be used to read the bufferedImage in a array of bits 
    inStream = clientSocket.getInputStream(); 

    // This is the outStream used to send messaages to the server 
    outStream = new PrintStream(clientSocket.getOutputStream()); 
} 



Это метод запуска внутри CameraConnection:

@Override 
public void run() { 

    String msg; 

    while (inStream_scanner.hasNext()) { 

     // Stores the incoming message and prints it 
     msg = inStream_scanner.nextLine(); 
     System.out.println(msg); 

     // Irrelevant 
     if (msg.equals("NOTIFY_MOTION")) { 
      onMotion(); 
     } 

     // Here is where the image gets read 
     else if (msg.equals("SEND_FRAME")) { 

      Frame f = new Frame(); 

      long capturedTime = inStream_scanner.nextLong(); 

      try { 
       byte[] sizeAr = new byte[4]; 
       inStream.read(sizeAr); 
       int size = ByteBuffer.wrap(sizeAr).asIntBuffer().get(); 
       byte[] imageAr = new byte[size]; 
       inStream.read(imageAr); 
       BufferedImage image = null; 
       image = ImageIO.read(new ByteArrayInputStream(imageAr)); 
       long receivedTime = System.currentTimeMillis(); 

       // Prints out the image dimension and the time in which it was received 
       System.out.println("Received " + image.getHeight() + "x" + image.getWidth() + ": " + receivedTime); 
       f.setCaptureTime(capturedTime); 
       f.setFrame(image); 
       f.setRecievedTime(receivedTime); 

      } catch (Exception e) { 
       System.out.println(e.toString()); 
      } 
     } 
    } 
} 



Выход:



Как уже упоминалось выше, он отлично работает в течение нескольких кадров, то он останавливается без исключения, а также сканер с InputStream начинает читать и печатать странные символы на консоль, как будто она повреждена. Он продолжает печатать эти странные символы, пока сервер продолжает отправлять фреймы. Вот изображение на выходе: screenshot from the output

+0

@HovercraftFullOfEels Здесь нет кода [tag: swing]. – EJP

+0

Да, я не включил код поворота, потому что я смог воспроизвести проблему на консоли. –

ответ

1
  • Вы не можете смешивать два типа потока или читателя или писателя на тот же сокет. Буферизация полностью испортит вас. Вам нужно использовать потоки объектов для всего.
  • Вы не можете предположить, что read() заполняет буфер.
  • Для чтения 4-байтового целого вы должны использовать readInt()writeInt() для его написания), а не код доморощенного.
  • Для чтения тела изображения вы должны использовать readFully().
  • Я не вижу необходимости в потоках объектов здесь: вы должны использовать DataInputStream и DataOutputStream.
+0

Чтобы убедиться, что я правильно понял, вы говорите, что сокет будет использоваться для чтения или записи, а не для обоих, не так ли? что, если я хочу отправлять и получать данные? Должен ли я сделать 2 соединения сокета? –

+0

хорошо, я решил проблему, используя потоки объектов только для обеих сторон. Огромное спасибо. –