套接字编程:输入流被破坏而没有异常

时间:2016-04-11 02:52:38

标签: java sockets client-server serversocket

我想要实现的目标:

我试图制作一个非常简单的摄像监控系统。在这种情况下,相机将是服务器,并且将有一个客户端应用程序在另一端观看视频输入。

为简单起见,我将通过捕获已保存视频文件中的帧来模拟相机,并通过套接字将这些帧逐个发送到所有连接的客户端( 是的,相机可以处理超过一个客户 )。在客户端,我将收到帧,然后我将一个接一个地在jPanel中显示它们以创建视频播放的效果。

我已经完成了所有这些,但它仅适用于几帧,然后突然停止而没有例外。


服务器端:


这是相机类中的主要功能:

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")并相应地执行一些操作。这些行为及其所做的与问题无关,因此我们可以放心地忽略它们。这是run方法:

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



这是 ClientConnection 类中的 sendFrame 方法。每当新帧可用并准备发送时,它就会被调用。

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



客户端:


这是主要的方法,它只是创建一个新的 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 中的run方法:

@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

1 个答案:

答案 0 :(得分:1)

  • 您不能在同一个套接字上混用两种类型的流或读取器或编写器。缓冲会让你彻底搞砸。您需要将对象流用于所有内容。
  • 您不能假设read()填充缓冲区。
  • 要读取4字节整数,您应该使用readInt()(和writeInt()进行编写),而非本土代码。
  • 要阅读图片正文,请使用readFully()
  • 我认为此处不需要对象流:您应该使用DataInputStreamDataOutputStream