【问题标题】:Errors when reading from ObjectInputStream Java从 ObjectInputStream Java 读取时出错
【发布时间】:2013-07-28 19:32:46
【问题描述】:

我正在尝试用 Java 编写 RemoteDesktop 的实现。我在套接字上使用 ObjectOutputStream 和 ObjectInputStream 来发送数据。为了发送数据,我使用了我创建的一个名为“Packet”的类:

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import javax.swing.ImageIcon;

/**
 * Holds data to send over network connection
 */
class Packet<T extends Serializable> implements Serializable {

    private T payload;

    public Packet() {
        super();
    }

    public Packet(T data) {
        super();
        setPayload(data);
    }

    public T getPayload() {
        return payload;
    }

    public void setPayload(T payload) {
        this.payload = payload;
    }

    public static void send(String message, ObjectOutputStream out) throws IOException {
        out.writeObject(new Packet<>(message));
        out.flush();
    }

    public static void send(Integer value, ObjectOutputStream out) throws IOException {
        out.writeObject(new Packet<>(value));
        out.flush();
    }

    public static void send(Block block, ObjectOutputStream out) throws IOException {
        out.writeObject(new Packet<>(block));
        out.flush();
    }

    public static void send(BufferedImage[][] images, ObjectOutputStream out) throws IOException {
        //convert to ImageIcon
        ImageIcon icons[][] = new ImageIcon[images.length][images[0].length];
        for (int x = 0; x < images.length; x++) {
            for (int y = 0; y < images[0].length; y++) {
                icons[x][y] = new ImageIcon(images[x][y]);
            }
        }
        out.writeObject(new Packet<>(icons));
        out.flush();
    }
}

第一次建立连接时,服务器会发送一个二维 ImageIcon 数组,其中包含屏幕的不同“块”。屏幕被分成这组块。然后,服务器会定期截取屏幕截图,并将屏幕的每个“块”与最后一个进行比较,看看它是否发生了变化。如果有变化,那么服务器会将新的屏幕区域发送到一个名为“Block”的类中,该类保存该块的 x 和 y 坐标以及 ImageIcon:

import java.awt.image.BufferedImage;
import java.io.Serializable;
import javax.swing.ImageIcon;

/**
 * Holds an image and its x and y coordinates on the screen
 */
class Block implements Serializable {

    private ImageIcon img;
    private int x;
    private int y;

    public Block() {
        super();
    }

    public Block(BufferedImage image, int x, int y) {
        img = new ImageIcon(image);
        this.x = x;
        this.y = y;
    }

    public Block(ImageIcon image, int x, int y) {
        img = image;
        this.x = x;
        this.y = y;
    }

    public int getx() {
        return x;
    }

    public int gety() {
        return y;
    }

    public ImageIcon getImage() {
        return img;
    }
}

这是服务器的主要代码:

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {

    public static void main(String args[]) throws IOException {

        ServerSocket s = new ServerSocket(5000);
        do {
            Socket c;
            ObjectOutputStream out;
            ObjectInputStream in;

            //start listening
            echo("Listening on port: 5000");

            //accept connection
            c = s.accept();
            echo("Connected to client at address " + c.getInetAddress().getHostAddress());

            //open IO streams
            out = new ObjectOutputStream(c.getOutputStream());
            in = new ObjectInputStream(c.getInputStream());

            ServerSession rdsession = new ServerSession(in, out); //start session
        } while (true);
    }

    private static void err(String message) {
        //prints error message and exits
        System.err.println(message);
        System.exit(1);
    }

    private static void echo(String message) {
        //prints message
        System.out.println(message);
    }
}

因此,当连接建立时,服务器会创建一个“ServerSession”类的实例。此类通过确定何时在“块”对象中发送新图像来处理 RD 会话。当它需要向客户端更新一个块时,它使用 Packet.send(Block, ObjectOutputStream) 方法。

对象使用以下代码从流中读取对象:

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import javax.swing.ImageIcon;

public class Client {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Socket c;
        ObjectOutputStream out;
        ObjectInputStream in;
        String serverIP = "127.0.0.1"; //replace with server IP
        int port = 5000;

        //open connection and streams
        c = new Socket(serverIP, port);
        out = new ObjectOutputStream(c.getOutputStream());
        in = new ObjectInputStream(c.getInputStream());

        ClientSession cs = null;

        do {
                Packet<?> p;
                Object o = in.readObject();
                if (o instanceof Packet) {
                    p = (Packet<?>) o;
                } else {
                    continue;
                }
                if (p.getPayload() instanceof String) { //check if string
                    echo("Server>" + p.getPayload());
                } else if (p.getPayload() instanceof Block) { //check if block
                    Block b = (Block) p.getPayload();
                    if (cs != null) {
                        cs.setImage(b.getImage(), b.getx(), b.gety());
                    }
                } else if (p.getPayload() instanceof ImageIcon[][]) { //check if 2-D image array
                    cs = new ClientSession(in, out, (ImageIcon[][]) p.getPayload()); //create session with image array
                }
        } while (true);
    }

    private static void err(String message) {
        System.err.println(message);
        System.exit(1);
    }

    private static void echo(String message) {
        System.out.println(message);
    }
}

ClientSession 类只存储屏幕图像并处理 GUI。

当我同时运行服务器和客户端时,在发送初始 ImageIcon[][] 数组后,每次客户端尝试读取“块”实例时都会出现无数错误。这些是经常抛出的错误:

1:

Exception in thread "main" java.io.InvalidClassException: Block; invalid descriptor for field 
    at java.io.ObjectStreamClass.readNonProxy(ObjectStreamClass.java:710)
    at java.io.ObjectInputStream.readClassDescriptor(ObjectInputStream.java:828)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1599)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1515)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1769)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1989)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1913)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1796)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
    at Client.main(Client.java:46)
Caused by: java.lang.IllegalArgumentException: illegal signature
    at java.io.ObjectStreamField.<init>(ObjectStreamField.java:122)
    at java.io.ObjectStreamClass.readNonProxy(ObjectStreamClass.java:708)
    ... 11 more

2:

Exception in thread "main" java.io.InvalidClassException: Block; invalid descriptor for field sq ~ sq
    at java.io.ObjectStreamClass.readNonProxy(ObjectStreamClass.java:710)
    at java.io.ObjectInputStream.readClassDescriptor(ObjectInputStream.java:828)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1599)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1515)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1769)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1989)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1913)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1796)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1989)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1913)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1796)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1989)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1913)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1796)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
    at Client.main(Client.java:25)
Caused by: java.lang.IllegalArgumentException: illegal signature
    at java.io.ObjectStreamField.<init>(ObjectStreamField.java:122)
    at java.io.ObjectStreamClass.readNonProxy(ObjectStreamClass.java:708)
    ... 19 more

3:

Exception in thread "main" java.io.EOFException
    at java.io.DataInputStream.readInt(DataInputStream.java:392)
    at java.io.ObjectInputStream$BlockDataInputStream.readInt(ObjectInputStream.java:2818)
    at java.io.ObjectInputStream.readInt(ObjectInputStream.java:969)
    at javax.swing.ImageIcon.readObject(ImageIcon.java:481)
    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1017)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1891)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1796)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1989)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1913)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1796)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1989)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1913)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1796)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
    at Client.main(Client.java:25)

4:

Exception in thread "main" java.io.StreamCorruptedException: invalid type code: 73
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1769)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1989)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1913)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1796)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1989)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1913)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1796)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
    at Client.main(Client.java:25)

感谢您抽出宝贵时间阅读这篇文章。任何建议将不胜感激。谢谢!

【问题讨论】:

  • 你的问题很长。你能把它精简为一个问题吗?
  • 发布 SSCCE 会有所帮助:sscce.org
  • clientsession 对输入输出流做了什么?
  • @jtahlborn 现在什么都没有,构造函数甚至不使用它们。它们只是为了将来将鼠标 x 和 y 发送到服务器。
  • 序列化对象是否包含内部类?如果虚拟机不同,这可能是原因之一。

标签: java


【解决方案1】:

在花了很多时间检查我的代码后,我确定问题与我在服务器上实现多线程的方式有关。线程将尝试同时写入输出流,这导致流损坏。谢谢大家的意见。

【讨论】:

    【解决方案2】:

    您的序列化和数据传输/交换不同步 - 客户端不再对齐和读取,服务器发送(序列化)有效对象的位置开始。

    这大概就是你得到 InvalidClassException: Block; 的原因。字段和 StreamCorruptedException 的描述符无效。

    您的数据传输代码主要依赖于序列化和 ImageIcon。

    并不是说这种方法是错误的,但它需要许多假设才能起作用:

    1. 类版本完全相同,
    2. SerialVersion UID 匹配,
    3. ImageIcon 是可序列化的,
    4. ImageIcon内部数据平台间兼容,
    5. 序列化和小/大端格式兼容。

    尝试去掉部分(比如 ImageIcon 的东西),看看你是否可以保持链接同步和良好的顺序。一旦您确定了导致链接不同步的原因,您就可以调查原因。

    【讨论】:

    • (1) 不是必需的。 (3) 在 Javadoc 中有说明。 (5) 由对象序列化规范保证。我同意您关于不同步的建议,但我没有在代码中看到可能发生这种情况的任何地方。
    • 哈哈,我同意 ;) 但是,除非另有证明,否则我更愿意对问题保持一个完全广泛的观点。这应该有助于他的理解和调查,了解他所依赖的所有因素..
    • (5) 在内部类中,ser 规范是(或曾经)不完美的。 diff 虚拟机的行为(d)不同
    【解决方案3】:

    您的 Block.class 文件有问题。我会重新编译,重新部署到两端,然后重新测试。

    EOFException 是意料之中的:您试图读取ObjectInputStream 的末尾,即当对等方关闭了他的连接端时。你需要单独抓住它才能离开你的do/while (true)

    NB 您应该在启动线程的run() 方法中创建服务器端ObjectOutputStreamObjectInputStream。目前,您正在accept() 循环内创建它们,并且它们都执行可以阻止接受线程接受其他客户端的 I/O。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-04-10
      • 2018-05-20
      • 1970-01-01
      • 1970-01-01
      • 2015-07-07
      • 2011-01-19
      • 1970-01-01
      相关资源
      最近更新 更多