Pessoal, estou tendo problemas em desserializar um objeto… Mas especificamente, em acessar seus atributos. Alguem pode me ajudar ?
Data.class:
public class Data implements Serializable {
private static byte sequenceNumber[] = new byte[4];
private static byte ACK[] = new byte[4];
private static byte connectionID[] = new byte[2];
private static byte notUsed[] = new byte[2];
public Data(int seq, int ack, int id, int used) {
sequenceNumber[3] = seq;
ACK[3] = ack;
connectionID[1] = id;
notUsed[1] = used;
}
//getters e setters
...
}
Cliente.class:
private void converteObjetoEmByte() throws Exception {
//criando objeto da classe Data
Data objeto = new Data(123456, 0, 0, 2);
byte bytes[] = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream( baos );
oos.writeObject( objeto );
oos.flush();
oos.close();
baos.close();
bytes = baos.toByteArray();
//enviando objeto
DatagramSocket pacote = new DatagramSocket(
bytes,
bytes.length,
//endereço do servidor
//porta
);
}
Servidor.class
//os bytes do pacote(objeto) foram recebidos
byte bytesDoPacote[] ;
Data objetoDesserializado;
ByteArrayInputStream bais = new ByteArrayInputStream( bytesDoPacote );
ObjectInputStream ois = new ObjectInputStream( bais );
objetoDesserializado = ois.readObject();
objectIS.close();
byteArrayIS.close();
//O erro ocorre ao acessar os atributos
//Não importa o valor colocado antes da serialização,
//apos desserializar, o campo fica com 0
objetoDesserializado.getACK() == 0;
Pra resumir, vou explicar oq se passa nos trechos de código acima. Tenho uma classe Data, a qual será serializada.
A classe Cliente q cria um objeto da classe Data, transforma em bytes e envia a um servidor.
A classe Servidor por sua vez, recebe os bytes e transforma o objeto(desserializa) mas ao acessar o atributos do objeto desserializado, eles contem valor nulo mesmo sendo setados antes de serializar. Alguem pode me ajudar a entender o porque ?!
Quando você não declara o serialVersionUID, o compilador cria esta constante em tempo de compilação.
Mas é recomendado declarar ela e ir alterando o valor a medida que a classe tem mais atributos adicionados.
Vamos lá, estou tentando enviar pacotes de um Cliente para um Servidor local. cada pacote deve conter os seguintes campos:
Sequence Number (32 bits)
Ack Number (32 bits)
Connection ID (16 bits)
Not used (9 bits): Deve ser zero.
A (ACK, 1 bit): Indica que o valor do campo AckNumber é válido.
S (SYN, 1 bit): Sincroniza os números de sequencia, estabelecimento da conexão.
F (FIN, 1 bit): Sem mais dados do emissor, término da conexão.
Estes campos formam o Payload de um pacote, que basicamente contém informações sobre o pacote enviado…
Quanto aos atributos estáticos, eu os defini assim por achar q eles devem pertencer somente aquela classe, apenas.
E sobre os getters, eles retornam um array de bytes, não há nenhum tipo de tratamento, apenas o retorno em bytes[ ]
Tem certeza que é isso mesmo?
A menor unidade de processamento em Java é um byte ou um boolean e ambos tem magnitude de 8 bits.
Mas você tem instruções com 1 bit.
Sim ! Está nas especificações do projeto… O Payload soma 12 bytes de dados fora os dados do que se quer enviar. Apenas uma correção, Not Used (13 bits): Deve ser zero.*
Agora sim, você postou que not used eram 9 bits e não 13.
Sequence Number (32 bits) ----> utilize uma variável do tipo int
Ack Number (32 bits) ----> utilize uma variável do tipo int
Connection ID (16 bits) ----> utilize uma variável do tipo short
Not used (13 bits): --+-> para estes 4 campos utilize uma única variável do tipo short
|
A (ACK, 1 bit): --+ faça bitmasking para ligar/desligar os 3 primeiros bits (F, S e A)
|
S (SYN, 1 bit): --+
|
F (FIN, 1 bit): --+
Atributos estáticos não são serializados, pois eles pertencem à classe e não ao objeto.
De qualquer forma, nesse seu caso eu não utilizaria a interface Serializable, pois por padrão o método writeObject grava alguns metadados para saber qual classe deve ser instanciada ao fazer o readObject, ou seja, você vai serializar muito mais bytes do que realmente quer.
Eu criaria um método toByteArray na classe Data, para retornar o array de bytes a serem serializados e um construtor que recebe um array de bytes como parâmetro para inicializar os atributos através de um array de bytes recebido.
Muito bom @staroski ! Ao ler as especificações dos atributos, não passou pela minha cabeça o uso dos tipos primitivos int e short e sim o de array de bytes, enfim, muito obrigado, deu uma bela simplificada.
Mais uma pergunta, já que os dados Not used são do tipo short, eu poderia simplesmente atribuir um valor ? Já que eu vou somente mudar os últimos 3 bits, eu teria então valores 1, 2 e 4 para setar. Não conheço o bitmask mas vou procurar sobre.
Enquanto a desserialização, os atributos agora não são mais estáticos, não sabia que dessa forma eles não poderiam ser serializados. E sobre a interface Serializable, a aplicação retornou uma exception onde pedia a implementação da interface. No mais, tudo funcionou, obrigado novamente !
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class Data {
private static final int ACK_BIT = 2; // posição do bit ACK
private static final int SYN_BIT = 1; // posição do bit SYN
private static final int FIN_BIT = 0; // posição do bit FIN
private int sequenceNumber;
private int ackNumber;
private short connectionID;
private short bitmask;
/**
* Cria um objeto Data
*/
public Data() {}
/**
* Cria um objeto Data inicializando os atributos a partir do array informado
*/
public Data(byte[] bytes) {
ByteArrayInputStream input = new ByteArrayInputStream(bytes);
DataInputStream dataReader = new DataInputStream(input);
try {
sequenceNumber = dataReader.readInt();
ackNumber = dataReader.readInt();
connectionID = dataReader.readShort();
bitmask = dataReader.readShort();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Converte este objeto Data em um array de bytes
*/
public byte[] toByteArray() {
ByteArrayOutputStream output = new ByteArrayOutputStream();
DataOutputStream dataWriter = new DataOutputStream(output);
try {
dataWriter.writeInt(sequenceNumber);
dataWriter.writeInt(ackNumber);
dataWriter.writeShort(connectionID);
dataWriter.writeShort(bitmask);
} catch (IOException e) {
e.printStackTrace();
}
return output.toByteArray();
}
/**
* Obtém a representação textual deste objeto
*/
@Override
public String toString() {
return String.format("%32s%32s%16s%16s",
Integer.toBinaryString(sequenceNumber),
Integer.toBinaryString(ackNumber),
Integer.toBinaryString(connectionID),
Integer.toBinaryString(bitmask))
.replace(' ', '0');
}
public void setSequenceNumber(int sequenceNumber) {
this.sequenceNumber = sequenceNumber;
}
public int getSequenceNumber() {
return sequenceNumber;
}
public void setAckNumber(int ackNumber) {
this.ackNumber = ackNumber;
}
public int getAckNumber() {
return ackNumber;
}
public void setConnectionID(short connectionID) {
this.connectionID = connectionID;
}
public short getConnectionID() {
return connectionID;
}
/**
* Liga/desliga o bit ACK
*/
public void setAck(boolean ligado) {
if (ligado) {
bitmask |= (1 << ACK_BIT);
} else {
bitmask &= ~(1 << ACK_BIT);
}
}
/**
* Verifica se o bit ACK está ligado
*/
public boolean isAck() {
return (bitmask & (1 << ACK_BIT)) == 1;
}
/**
* Liga/desliga o bit SYN
*/
public void setSyn(boolean ligado) {
if (ligado) {
bitmask |= (1 << SYN_BIT);
} else {
bitmask &= ~(1 << SYN_BIT);
}
}
/**
* Verifica se o bit SYN está ligado
*/
public boolean isSyn() {
return (bitmask & (1 << SYN_BIT)) == 1;
}
/**
* Liga/desliga o bit FIN
*/
public void setFin(boolean ligado) {
if (ligado) {
bitmask |= (1 << FIN_BIT);
} else {
bitmask &= ~(1 << FIN_BIT);
}
}
/**
* Verifica se o bit FIN está ligado
*/
public boolean isFin() {
return (bitmask & (1 << FIN_BIT)) == 1;
}
}