Youssef Negm
Youssef Negm

Reputation: 64

Socket Programming: The input stream gets corrupted without an exception

What I'm trying to achieve:

I'm trying to make a very simple camera surveillance system. In this case, the camera will be the server, and there will be a client application to watch the video feed on the other end.

For simplicity sake, I will emulate the camera by capturing frames from a saved video file, and sending these frames one by one through sockets to all the connected clients (Yes, the camera can handle more than one client). On the client side, I will receive the frames and then I will display them in a jPanel one after another to create the effect of a video playing.

I've already done all that but it works only for a couple of frames then it suddenly stops without an exception.


The Server-Side:


This is the main function in the Camera class:

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);

    }

}



Snippets from the ClientConnection class:

The constructor:

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



The ClientConnection class implements the runnable interface so it can work on a separate thread. The run method will be responsible for receiving predefined messages (ex. "SET_MOVIE") from the client and do some actions accordingly. These actions and what they do are irrelevant to the question so we can safely ignore them. Here is the run method:

@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);
    }
}



This is the sendFrame method in the ClientConnection class. It gets called every time a new frame is available and ready to be sent.

// 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();
}



The Client-Side:


This is the main method, it simply creates a new CameraConnection and run it on it's own thread.

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



This is the CameraConnection constructor:

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());
}



This is the run method inside the 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());
            }
        }
    }
}



The Output:



As mentioned above, it works fine for a couple of frames then it stops without an exception, also the scanner from the inputStream starts reading and printing weird symbols on the console as if it's corrupted. It keeps printing these weird symbols as long as the server keeps sending the frames. Here is an image to the output: screenshot from the output

Upvotes: 0

Views: 1184

Answers (1)

user207421
user207421

Reputation: 310860

  • You can't mix two types of stream or reader or writer on the same socket. Buffering will foul you up completely. You need to use the object streams for everything.
  • You can't assume that read() fills the buffer.
  • For reading a 4-byte integer you should use readInt() (and writeInt() for writing it), not home-grown code.
  • For reading the body of the image you should use readFully().
  • I don't see any need for object streams here: you should use DataInputStream and DataOutputStream.

Upvotes: 1

Related Questions