【问题标题】:Java - serializable and sending object via socketJava - 通过套接字可序列化和发送对象
【发布时间】:2011-06-21 08:43:19
【问题描述】:

我收到一个错误:

IOException on socket listen: java.io.NotSerializableException: java.net.Socket

我尝试使用此代码通过套接字发送对象:

ObjectOutputStream outObjects = new  ObjectOutputStream(socket.getOutputStream());
outObjects.writeObject(currentPlayer);

output.flush();

第二行给出错误....但我已经序列化(实现 Serializable)类 Player(currentPlayer 对象的类) 但是 Player 类的成员之一是 Socket 对象..... 我尝试重新定义和序列化套接字对象,但无法解决问题.... 我哪里错了?

【问题讨论】:

  • 显示currentPlayer的代码

标签: java serializable


【解决方案1】:

您不能序列化 Socket 对象,因此您应该使套接字字段瞬态。如果您需要一些套接字属性,您可以在 Player 中添加额外的字段来表示这些字段。

【讨论】:

  • 谢谢!我在想那个:(但我希望它在某种程度上是可能的:)因为据说几乎所有的类都实现了可序列化,所以我会去掉 addr、localport 和 port THX!
【解决方案2】:

表示Socket类不可序列化。

你必须将 Socket 成员设置为瞬态,然后它不会被序列化。

请务必在私有的 readObject(ObjectInputStream) 方法中重新创建 Socket 对象,否则会遇到空指针异常。

【讨论】:

    【解决方案3】:

    implements Serializable 本身并不使对象可序列化。对象本身中的任何和所有序列化字段都必须是可序列化的(以及它们包含的任何和所有字段)。

    由于套接字不可序列化,您不应尝试通过线路发送它。您可以通过将其声明为 transient 将其排除在序列化之外。反序列化的对象当然没有套接字,因此您可能还需要检查对象中将用于序列化和反序列化的可选 readObject 和 writeObject 方法。

    查看this article on serialization

    【讨论】:

      【解决方案4】:

      套接字不可序列化,因为它与正在执行的机器、操作系统等相关联。要可序列化,对象的类必须实现可序列化,并且必须包含所有可递归地序列化的对象。

      因此,要序列化您的播放器,您必须从序列化中排除其套接字属性。你可以通过声明属性transient来做到这一点:

      private transient Socket mySocket;
      

      当播放器被反序列化时,它的套接字将因此为空,它很有可能无法正常工作。您可能应该检查您的设计,因为序列化需要套接字才能工作的对象可能没有意义。

      【讨论】:

        【解决方案5】:

        有几种方法可以解决这个问题,最快的方法是将您的 Socket 成员标记为transient。这可确保 java 的默认序列化将忽略 Socket,并且您的副本应包含 null。

        private transient Socket sock;
        

        您也可以提供自己的序列化方法,这需要您自己编写和读取所有成员。您可以使用Serializable 中定义的方法(见下文)或实现Externalizable

         private void writeObject(java.io.ObjectOutputStream out)
             throws IOException
         private void readObject(java.io.ObjectInputStream in)
             throws IOException, ClassNotFoundException;
        

        最干净的方法是将您的 Players 状态与您的网络代码分开,从而生成一个只有 Serializable 成员的类。作为奖励,发送类不再包含无效的成员字段。

        【讨论】:

          【解决方案6】:

          编辑回答评论

          尝试将套接字成员声明为transient,如

          private transient Socket socket;
          

          这将阻止序列化机制尝试发送socket 的值。请注意,您不能将持有值的对象有意义地发送到另一个进程,这些值代表 OS 资源(如套接字句柄)。在最好的情况下,任何在接收端使用此类对象的尝试都会导致抛出异常。在最坏的情况下,这样的尝试可能会引入细微的错误(数据被发送给错误的用户)。出于这个原因,这样的对象永远不能序列化(除非有人犯了错误)。

          考虑以下(简化的)示例:在 UNIX 上,套接字由一个整数(所谓的文件描述符)表示,该整数在创建套接字时由操作系统返回。如果将此值发送到另一个进程,则无法保证该数字实际上是指在接收进程中打开的有效文件句柄。更糟糕的是,如果接收进程中存在具有相同数值的文件句柄,则几乎不可能引用发送进程中打开的同一个套接字。所以,如果这个数字在发送数据的时候实际上是在接收过程中作为文件描述符使用的,那么它几乎肯定会去到除了预期目的地之外的任何地方。

          如果发送和接收进程在同一台机器上,种方法可以将“套接字”从一个进程传输到另一个进程。但我怀疑是否有简单的方法可以从 Java 访问这些操作系统调用。

          【讨论】:

          • 但我需要将它与 Player 对象一起发送
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-11-26
          • 2017-03-20
          • 2014-01-11
          • 2021-01-11
          • 2011-05-14
          • 2017-04-24
          • 2012-08-29
          相关资源
          最近更新 更多