2010-10-22 7 views
10

Próbuję wysłać zserializowanego obiektu z procesu serwera do procesu klienta w Javie przy użyciu UDP. Problem polega na tym, że klient jest blokowany w metodzie odbierania. Czy ktoś może pomóc ?!Wysyłanie i odbieranie obiektu serializacji na UDP

Oto kod serwera do wysyłania obiektu:

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); 

i tu jest kod klienta do odbioru obiektu:

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

po prostu chcesz otrzymać przedmiot, aby móc zrekonstruować, ale nie mogę odebrać samego pakietu.

+0

może trzeba przepłukać bufor ... – ultrajohn

Odpowiedz

13

Nie wiem co chcesz osiągnąć w końcu, ale praca z UDP nie jest tak łatwo ... głównym powodem jest w Opisie Przedmiotu DatagramPacket:

pakiety strumieniowe są używane do implementuj usługę dostarczania pakietów bezpołączeniowych: . Każda wiadomość jest kierowana z jednej maszyny do innej, opartej wyłącznie na na informacji zawartej w pakiecie . Wiele pakietów wysłanych z z jednego komputera do drugiego może być inaczej kierowanych i może przybyć do dowolnej kolejki . Dostarczenie paczki nie jest gwarantowane na .

Dobry poradnik podczas pracy z UDP jest http://download.oracle.com/javase/tutorial/networking/datagrams/clientServer.html

O swojej blokowania:

otrzymuje pakiet datagramowy z tego gniazda . Gdy ta metoda zostanie zwrócona, bufor DatagramPacket zostanie wypełniony otrzymanymi danymi. Pakiet datagramowy zawiera również adres IP nadawcy, i numer portu na urządzeniu nadawcy .

Ta metoda blokuje do momentu otrzymania datagramu w postaci . Pole długości obiektu pakietów datagramów o nazwie zawiera długość odebranej wiadomości. Jeśli komunikat jest dłuższy niż długość pakietu , wiadomość jest obcięta.

ja naprawdę nie przetestować go, ale jestem całkiem pewny, że - na podstawie opisu - że funkcja datagramsocket.reseive zablokuje aż pakiet zostanie wypełniony (w przypadku aż 100000 bajtów są odbierane).

Proponuję rozpocząć od datagrampacket o stałej znanej długości, w której transmitowany jest rozmiar rzeczywistej ładowności.Coś jak:

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(); 
    } 
} 

Z drugiej strony teraz znać swoje rozmiary:

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(); 
    } 
} 

clas CientModel sI używane:

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

    String name = ""; 
    int data = 1; 

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

testowałem ten kod i to działa dobrze. Nadzieja, która pomaga (mam bajt-To-int i okolice od http://www.tutorials.de/java/228129-konvertierung-von-integer-byte-array.html)

Edycja: jak stwierdzono w komentarzach, często bardzo złym pomysłem jest używanie UDP, głównie, ponieważ nie wiesz, czy twoje pakiety są odbierane we właściwej kolejności, a nawet w ogóle. UDP NIE gwarantuje tego. Nie robiłem zbyt wiele programowania udp, ale jedyną częścią, na której możesz polegać (jeśli dobrze zrozumiałem) jest to, że jeśli dostaniesz pakiet i pasuje on do datagramu (65 527 bajtów - patrz https://en.wikipedia.org/wiki/User_Datagram_Protocol) to będzie zawierał całość rzecz. Więc jeśli nie zależy ci na kolejności, w jakiej wiadomość przychodzi, a twój obiekt pasuje do datagramu, powinieneś być w porządku.

Edycja2: Jeśli chodzi o kod: nie używaj go jak jest. jest to tylko przykład, ind UDP powinieneś mieć tylko jeden typ pakietu, a ten o znanym rozmiarze. w ten sposób nie musisz wysyłać "rozmiaru". Jeśli użyjesz kodu pokazanego powyżej, a jeden pakiet zostanie upuszczony, następny pakiet będzie miał nieprawidłowy rozmiar (tzn. Pierwszy pakiet zostanie upuszczony, nagle sprawdzasz pierwsze bajty ładunku, aby uzyskać rozmiar).

+0

Nie jestem przetestowany, ale z opisu C recvfrom (2) wynika, że ​​odczyt z połączenia UDP może powrócić, gdy tylko cały datagram zostanie odczytany, a rzeczywista liczba odczytu bajtów jest zwracana (w przypadku JDK będzie to pole DatagramPacket.length). Sugeruję napisanie prostego klienta/serwera Java echo w celu przetestowania zachowania. –

+0

Datagramy mogą pojawić się w kolejności lub nigdy. Być może będziesz musiał potwierdzić początkowy pakiet, aby potwierdzić następny spodziewany rozmiar, a następnie potwierdzić pakiet ładunku – user12345613

+0

. Czy wysyłasz długość przed wysłaniem rzeczywistej ładowności? Jak to zmienia sytuację? Składnia w obu fragmentach kodu źródłowego dla transmisji ładunku pozostaje taka sama. Jestem zdezorientowany, oczywiście, – iGbanam

1

ja naprawdę nie przetestować go, ale jestem całkiem pewny, że - na podstawie opisu - że funkcja datagramsocket.reseive zablokuje aż pakiet zostanie wypełniony (w przypadku aż 100000 bajtów otrzymał).

To jest złe. Funkcja odbioru będzie blokować, dopóki nie zostanie odebrany datagram, który może być mniejszy niż rozmiar bufora (i zazwyczaj będzie). Metoda packet.getLength() powie Ci, jak duży był.

Powiązane problemy