Java - сериализуемый и отправляющий объект через сокет

Я получаю сообщение об ошибке:

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..... я пытаюсь переопределить и сериализовать объект сокета, но никак не решить проблему.... где я ошибаюсь?


person Mikeel    schedule 04.02.2011    source источник
comment
покажи код currentPlayer   -  person Bozho    schedule 04.02.2011


Ответы (6)


Вы не можете сериализовать объект Socket, поэтому вы должны сделать поле сокета переходным. Если вам нужны некоторые свойства сокета, вы можете поместить в Player дополнительные поля для представления этих полей.

person Gursel Koca    schedule 04.02.2011
comment
спасибо! Я думал об этом :( но я надеялся, что это каким-то образом возможно :), так как говорят, что почти все классы реализуют сериализуемость, поэтому я уберу адрес, локальный порт и порт, THX! - person Mikeel; 04.02.2011

Это означает, что класс Socket не сериализуем.

Вы должны установить элемент Socket как переходный, тогда он не будет сериализован.

Просто обязательно воссоздайте объект Socket в частном методе readObject(ObjectInputStream), иначе вы можете столкнуться с исключениями нулевого указателя.

person Thijs Wouters    schedule 04.02.2011

implements Serializable сам по себе не делает объект сериализуемым. Любые и все сериализованные поля в самом объекте, в свою очередь, должны быть сериализуемыми (и все поля, которые они содержат).

Поскольку сокет не сериализуем, вам не следует пытаться отправить его по линии. Вы можете исключить его из сериализации, объявив его как transient. Десериализованный объект, конечно, не имеет сокета, поэтому вы также можете проверить необязательные методы readObject и writeObject в вашем объекте, которые будут использоваться для сериализации и десериализации.

Ознакомьтесь с этой статьей о сериализации.

person extraneon    schedule 04.02.2011

Сокет нельзя сериализовать, потому что он привязан к машине, ОС и т. д., на которой он выполняется. Чтобы быть сериализуемым, класс объекта должен реализовывать Serializable и должен содержать объекты, которые все рекурсивно сериализуемы.

Таким образом, чтобы сериализовать ваш плеер, вы должны исключить его атрибут сокета из сериализации. Вы делаете это, объявляя атрибут transient :

private transient Socket mySocket;

Таким образом, когда плеер будет десериализован, его сокет будет нулевым, и есть большая вероятность, что он не будет работать правильно. Вероятно, вам следует пересмотреть свой дизайн, потому что сериализация объекта, которому для работы требуется сокет, вероятно, не имеет смысла.

person JB Nizet    schedule 04.02.2011

Есть несколько способов решить эту проблему, самым быстрым будет пометка вашего члена 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. В качестве бонуса класс отправки больше не содержит недопустимых полей-членов.

person josefx    schedule 04.02.2011

(Отредактировано, чтобы ответить на комментарий)

Попробуйте объявить элемент сокета как transient, как в

private transient Socket socket;

Это предотвратит попытку механизма сериализации отправить значение socket. Обратите внимание, что вы не можете осмысленно отправлять объекты, содержащие значения, которые представляют ресурсы ОС, такие как дескрипторы сокетов, другому процессу. В лучшем случае любая попытка использовать такой объект на принимающей стороне приведет к возникновению исключения. В худшем случае такая попытка может привести к скрытым ошибкам (данные отправляются не тому пользователю). По этой причине такие объекты никогда не сериализуются (кроме случаев, когда кто-то допустил ошибку).

Рассмотрим следующий (упрощенный) пример: в UNIX сокет представлен целым числом (так называемый файловый дескриптор), которое возвращается ОС при создании сокета. Если это значение отправляется другому процессу, нет никаких гарантий, что это число действительно относится к действительному дескриптору файла, открытому в принимающем процессе. Хуже того, если в процессе-получателе есть дескриптор файла с таким же числовым значением, почти невозможно сослаться на тот же сокет, открытый в процессе-отправителе. Таким образом, если число фактически используется в качестве дескриптора файла в процессе приема при отправке данных, оно почти наверняка попадет куда угодно, кроме предполагаемого места назначения.

Если отправляющий и принимающий процессы находятся на одном компьютере, существуют способы передачи "сокета" от одного процесса к другому. Но я сомневаюсь, что есть простые способы доступа к этим вызовам ОС из Java.

person Dirk    schedule 04.02.2011
comment
но мне нужно отправить его с объектом Player - person Mikeel; 04.02.2011