2010-10-22 14 views
10

Sto tentando di inviare un oggetto serializzato da un processo server a un processo client in Java utilizzando UDP. Il problema è che il client è bloccato sul metodo di ricezione. Qualcuno può aiutare ?!Invia e ricevi oggetto serialize su UDP

Ecco il codice del server per l'invio dell'oggetto:

ClientModel C1= new ClientModel(100,"Noor","Noor",38,38,"asd"); 
    ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
    ObjectOutputStream oos = new ObjectOutputStream(baos); 
    oos.writeObject(C1); 
    oos.flush(); 
    byte[] Buf= baos.toByteArray(); 
    packet = new DatagramPacket(Buf, Buf.length, client, port); 
    socket.send(packet); 

e qui è il codice del client per ricevere l'oggetto:

byte[] buffer = new byte[100000]; 
packet = new DatagramPacket(buffer, buffer.length); 
socket.receive(packet); 
System.out.println("packet received"); 

Voglio solo di ricevere l'oggetto da essere in grado di ricostruire ma non posso ricevere il pacchetto stesso.

+0

forse è necessario svuotare il buffer ... – ultrajohn

risposta

13

io non so cosa si vuole realizzare, alla fine, ma lavorare con UDP non è così facile ... la ragione principale è nella descrizione dell'oggetto DatagramPacket:

pacchetti Datagram sono utilizzati per attuare un servizio di consegna senza connessione servizio. Ogni messaggio viene instradato da da un computer all'altro basato esclusivamente su sulle informazioni contenute nel pacchetto . Più pacchetti inviati da un computer a un altro potrebbero essere instradati in modo diverso da e potrebbero arrivare in qualsiasi ordine . La consegna del pacchetto non è garantita .

Un buon tutorial quando si lavora con UDP è http://download.oracle.com/javase/tutorial/networking/datagrams/clientServer.html

Informazioni sul blocco:

riceve un pacchetto datagramma da questa presa . Quando viene restituito questo metodo, il buffer DatagramPacket viene riempito con i dati ricevuti. Il pacchetto datagramma contiene anche l'indirizzo IP del mittente, e il numero di porta sulla macchina del mittente.

Questo metodo blocca fino a quando un datagramma è ricevuto. Il campo lunghezza dell'oggetto pacchetto datagramma contiene la lunghezza del messaggio ricevuto. Se il messaggio è più lungo della lunghezza del pacchetto, il messaggio viene troncato.

Io non ha ancora veramente provarlo, ma sono abbastanza sicuro che - in base alla descrizione - che la funzione datagramsocket.reseive bloccherà fino a quando si riempie il pacchetto (nel tuo caso fino a quando vengono ricevuti 100000 byte).

Suggerirei di iniziare con un datagrampacket con una lunghezza nota fissa, in cui si trasmette la dimensione del carico utile effettivo.Qualcosa di simile:

public static void main(String[] args) { 
    ClientModel c1 = new ClientModel(); 
    c1.data = 123; 
    c1.name = "test"; 

    try { 
     ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
     ObjectOutputStream oos = new ObjectOutputStream(baos); 
     oos.writeObject(c1); 
     oos.flush(); 
     // get the byte array of the object 
     byte[] Buf= baos.toByteArray(); 

     int number = Buf.length;; 
     byte[] data = new byte[4]; 

     // int -> byte[] 
     for (int i = 0; i < 4; ++i) { 
      int shift = i << 3; // i * 8 
      data[3-i] = (byte)((number & (0xff << shift)) >>> shift); 
     } 

     DatagramSocket socket = new DatagramSocket(1233); 
     InetAddress client = InetAddress.getByName("localhost"); 
     DatagramPacket packet = new DatagramPacket(data, 4, client, 1234); 
     socket.send(packet); 

     // now send the payload 
     packet = new DatagramPacket(Buf, Buf.length, client, 1234); 
     socket.send(packet); 

     System.out.println("DONE SENDING"); 
    } catch(Exception e) { 
     e.printStackTrace(); 
    } 
} 

D'altra parte ora si sa le dimensioni:

public static void main(String[] args) { 
    try { 
     DatagramSocket socket = new DatagramSocket(1234); 

     byte[] data = new byte[4]; 
     DatagramPacket packet = new DatagramPacket(data, data.length); 
     socket.receive(packet); 

     int len = 0; 
     // byte[] -> int 
     for (int i = 0; i < 4; ++i) { 
      len |= (data[3-i] & 0xff) << (i << 3); 
     } 

     // now we know the length of the payload 
     byte[] buffer = new byte[len]; 
     packet = new DatagramPacket(buffer, buffer.length); 
     socket.receive(packet); 

     ByteArrayInputStream baos = new ByteArrayInputStream(buffer); 
     ObjectInputStream oos = new ObjectInputStream(baos); 
     ClientModel c1 = (ClientModel)oos.readObject(); 
     c1.print(); 
    } catch(Exception e) { 
     e.printStackTrace(); 
    } 
} 

La clas CientModel sI utilizzato:

public class ClientModel implements Serializable{ 
    private static final long serialVersionUID = -4507489610617393544L; 

    String name = ""; 
    int data = 1; 

    void print() { 
     System.out.println(data +": " + name); 
    } 
} 

ho provato questo codice e funziona bene. Spero che questo aiuti (ho preso il byte-int e in giro da http://www.tutorials.de/java/228129-konvertierung-von-integer-byte-array.html)

Modifica: come indicato nei commenti, è spesso una pessima idea utilizzare UDP, principalmente, perché non si conoscono i pacchetti sono ricevuti nell'ordine corretto, o addirittura del tutto. UDP NON garantisce questo. Non ho fatto troppa programmazione di udp, ma l'unica parte su cui puoi fare affidamento (se ho capito bene) è che se ottieni un pacchetto e si adatta al datagramma (65.527 byte - vedi https://en.wikipedia.org/wiki/User_Datagram_Protocol) conterrà l'intero cosa. Quindi se non ti interessa l'ordine con cui il messaggio viene e il tuo oggetto si adatta al datagramma, dovresti stare bene.

Edit2: Come per il codice: non usarlo così com'è. è solo un esempio, ind UDP dovresti avere solo un tipo di pacchetto, e questo con una dimensione nota. in questo modo non è necessario inviare la "dimensione". Se si utilizza il codice come mostrato sopra, e un pacchetto viene eliminato, il pacchetto successivo avrà la dimensione sbagliata (ovvero il primo pacchetto viene rilasciato, improvvisamente si stanno verificando i primi byte del payload per ottenere la dimensione).

+0

Non sono ancora testato, ma dalla descrizione di C recvfrom (2), è evidente che la lettura dalla connessione UDP può tornare non appena viene letto l'intero datagramma e il numero effettivo di byte letti viene restituito (nel caso di JDK sarà nel campo DatagramPacket.length). Suggerisco di scrivere un semplice client/server echo Java per testare il comportamento. –

+0

I datagrammi possono arrivare fuori servizio o mai del tutto. Potrebbe essere necessario riconoscere il pacchetto iniziale per confermare la prossima dimensione prevista e quindi accettare il pacchetto di payload – user12345613

+0

Quindi si invia la lunghezza prima di inviare il carico utile effettivo? Come cambia la situazione? La sintassi in entrambi i passaggi del codice sorgente per la trasmissione del carico utile rimane la stessa. Sono confuso – iGbanam

1

io abbiamo mangiato veramente provarlo, ma sono abbastanza sicuro che - sulla base della descrizione - che la funzione datagramsocket.reseive bloccherà finché non viene riempito il pacchetto (nel tuo caso fino a 100000 byte sono ricevuti).

Questo è sbagliato. La funzione di ricezione bloccherà fino a quando non viene ricevuto un datagramma, che può essere più piccolo della dimensione del buffer (e di solito lo sarà). Il metodo packet.getLength() ti dirà quanto è grande.