2012-05-27 9 views
7

Tak więc w zasadzie piszę grę typu klient-serwer dla wielu graczy. Mam SeverCommunicationThread, który tworzy gameThread, jeśli otrzymuje RequestForGame tworzy gameThread. Kiedy wysyłam wyjątek RequestForGame java.io.StreamCorruptedException: niepoprawny kod typu: 00 Zakładam, że to dlatego, że oba wątki próbują odczytać ten sam ObjectInputStream, nie mam zbyt wiele zrozumienia, jak to działa, po prostu wiem jak używać tego. Czy możesz mi pomóc zrozumieć, na czym polega problem i jak to naprawić? Dzięki :)java.io.StreamCorruptedException: nieprawidłowy kod typu: 00

public class ServerCommunicationThread extends Thread{ 
private Socket connectionSocket; 
private ObjectInputStream inFromClient; 
private ObjectOutputStream outToClient; 
private String nickname; 
private ServerModelManager model; 


public ServerCommunicationThread(Socket connectionSocket, 
     ServerModelManager model) throws IOException { 
    this.connectionSocket = connectionSocket; 
    inFromClient = new ObjectInputStream(connectionSocket.getInputStream()); 
    outToClient = new ObjectOutputStream(connectionSocket.getOutputStream()); 
    this.model = model; 
    start(); 

} 

public void run() { 
    try { 
     String nickname = (String) inFromClient.readObject(); 
     if (model.exists(nickname)){ 
      System.out.println(nickname + " already exists"); 
      outToClient.writeObject(new MessageForClient("Please choose another nickname")); 
     } 
     else 
     { 
      System.out.println(nickname + " connected, adding to list"); 
      model.addClient(nickname, connectionSocket,outToClient,inFromClient); 
      this.nickname=nickname; 
     } 
     while(true){ 
      Object o= inFromClient.readObject();//StreamCorruptedexception 
      if(o instanceof RequestForGame) 
      { 
       RequestForGame r=(RequestForGame)o; 
       String userToPlayWith=r.getUserToPlayWith(); 
       if(userToPlayWith.equals(nickname)) 
       { 
        String message="Playing with yourself makes your palms hairy, choose another opponent"; 
        outToClient.writeObject(message); 
       } 
       else 
       { 
       System.out.println("received request to play with "+userToPlayWith+". starting game"); 
       ClientRepresentative client1=model.getClient(nickname); 
       ClientRepresentative client2=model.getClient(userToPlayWith); 
       ServerGameThread s=new ServerGameThread(client2,client1,client2.getInStream(),client1.getInStream(),client1.getOutStream(),client2.getOutStream()); 
       } 
      } 
      else if(o instanceof String) 
      { 
       String s=(String) o; 
       if(s.equals("i want to quit")) 
       { 
        model.deleteClient(nickname); 
        inFromClient.close(); 
        String q="quit"; 
        outToClient.writeObject(q); 
        connectionSocket.close(); 
        System.out.println(nickname+"has quit without exc"); 
       } 
      } 
     } 
    } catch (EOFException e) { 
     System.out.println(nickname+" has quit"); 
    } 
    catch (SocketException e) 
    { 
     System.out.println(nickname+" has quit"); 
    } 

    catch (Exception e) { 

     e.printStackTrace(); 
    } 
} 

} 
public class ServerGameThread extends Thread { 

private ClientRepresentative client1,client2; 
private ObjectInputStream inFromClient1,inFromClient2; 
private ObjectOutputStream outToClient1,outToClient2; 
private Field gameField; 
public ServerGameThread(ClientRepresentative client1, ClientRepresentative client2,ObjectInputStream inFromClient1,ObjectInputStream inFromClient2,ObjectOutputStream outToClient1,ObjectOutputStream outToClient2) 
{ 
    System.out.println("startin game thred"); 
    this.client1=client1;//client 1 goes first 
    this.client2=client2;//client 2 started game 


     this.inFromClient1=inFromClient1; 
     this.inFromClient2=inFromClient2; 
     this.outToClient1=outToClient1; 
     this.outToClient2=outToClient2; 


     gameField=new Field(); 
     System.out.println("check"); 
     start(); 
} 
public void run() 
{ 
    System.out.println("Starting game. players: "+client1.getNickname()+";"+client2.getNickname()); 
    try { 
     outToClient1.writeObject(gameField); 
     outToClient2.writeObject(gameField); 
     while(true) 
     { 
      try { 
       System.out.println("listening to "+client1.getNickname()); 
       Object o1=inFromClient1.readObject();//read move from client 1.**//StreamCorruptedexception** 

       while(!(o1 instanceof PlayerMove)) 
       { 
        o1=inFromClient1.readObject();//read move from client 1. 
       } 
       PlayerMove move1=(PlayerMove)o1; 
       System.out.println("received move "+move1+" sending to "+client2.getNickname()); 
       outToClient2.writeObject(move1); 
       System.out.println("listening to "+client2.getNickname()); 
       Object o2=inFromClient2.readObject();//read move from client 1. 
       while(!(o2 instanceof PlayerMove)) 
       { 
        o2=inFromClient2.readObject();//read move from client 1. 
       } 
       PlayerMove move2=(PlayerMove)o2; 
       System.out.println("received move "+move2+" sending to "+client1.getNickname()); 
       outToClient1.writeObject(move2); 
      } 
       catch (ClassNotFoundException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
     } 
    } catch (IOException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 

} 
}  

metoda model.addClient choć nie sądzę, że problem jest tutaj

public void addClient(String nickname, Socket  clientSocket,ObjectOutputStream stream,ObjectInputStream inStream) 
{ 
    clients.addClient(nickname, clientSocket,stream,inStream);//add to arraylist 
//send client list to all clients 
    String[] users=this.getAvailableClients(); 
    ObjectOutputStream[] streams=clients.getOutStreams(); 
    for(int i=0;i<streams.length;i++) 
    { 
     try { 
      streams[i].writeObject(users); 
     } catch (IOException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
    } 
} 

proxy po stronie klienta, który wysyła do serwera obiektów, metody są wywoływane przez działania użytkownika w GUI

public class Proxy { 
final int PORT = 1337; 
String host; 
String nickname; 
private Socket clientSocket; 
private ObjectOutputStream outToServer; 
private ObjectInputStream inFromServer; 
private ClientModelManager manager; 
public Proxy(String nickname,String host,ClientModelManager manager) 
{ 
    this.nickname=nickname; 
    this.host=host; 
    this.manager=manager; 
    this.connect(nickname); 
} 
public void connect(String nick) 
{ 
    Socket clientSocket; 
    try { 
     clientSocket = new Socket(host, PORT); 
     System.out.println("client socket created"); 
     outToServer = new ObjectOutputStream(clientSocket.getOutputStream()); 
     inFromServer=new ObjectInputStream(clientSocket.getInputStream()); 
     outToServer.flush(); 
     outToServer.writeObject(nick); 
     ClientReceiverThread t=new ClientReceiverThread(inFromServer,manager); 
     t.start(); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
} 
public void makeRequest(String user) 
{ 
    try 
    { 
    outToServer.writeObject(new RequestForGame(user)); 
    } 
    catch(IOException e) 
    { 
     e.printStackTrace(); 
    } 
} 
public void quit() 
{ 
    try { 
     outToServer.writeObject(new String("i want to quit")); 
     //clientSocket.close(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
} 
public void sendMove(PlayerMove move) 
{ 
    try { 
     outToServer.writeObject(move); 
    } catch (IOException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 
} 

}

+0

Co robi ServerModelManager podczas wywoływania 'model.addClient (pseudonim, connectionSocket, outToClient, inFromClient);'? Może w nim występować kod powodujący uszkodzenie strumienia. – Vulcan

+0

addClient (dodaje użytkownika do tablicy ArrayList typu ClientRepresantative przechowującej gniazdo, objectouput i strumień wejściowy). Nie czyta niczego. – user1420273

+0

W porządku. Powinieneś również opróżnić ObjectOutputStreams po ich skonstruowaniu, aby upewnić się, że nagłówek został wysłany. Może to być przyczyną tego błędu; nagłówek strumienia jeszcze się nie zakończył. Po utworzeniu opróżnij obiekt ObjectOutputStream po stronie klienta i serwera. – Vulcan

Odpowiedz

6

Ten problem może wystąpić, jeśli możesz zbudować nowy ObjectInputStream lub ObjectOutputStream w tym samym gnieździe, zamiast używać tych samych dla żywotności gniazda; jeśli używasz innego rodzaju strumienia na tym samym gnieździe; lub jeśli używasz strumieni obiektów do odczytywania lub zapisywania czegoś, co nie jest obiektem, a nie jesteś zsynchronizowany.

+1

Najpierw tworzę obiekt ObjectOutputStream (Proxy, ServerCommunicationThread). Buduję to tylko raz, a następnie po prostu przesyłaj odnośnik do tego drugiego wątku. Nie używam żadnego innego rodzaju strumienia. Jedyną rzeczą, która przychodzi do głowy jest to, że próbuję odczytać obiekt z tego samego ObjectInputStream w 2 wątkach w tym samym czasie. (ServerCommunicationThread, ServerGameThread) Czy to może być problem? (od lokalizacji wyjątku przypuszczam, że tak ..) – user1420273

+0

@ user1420273 Z pewnością może, chyba że masz odpowiednią synchronizację. – EJP

3

Może się to również zdarzyć, jeśli maszyna JVM odczytująca obiekt serializowany nie ma prawidłowych plików klasy/jar dla obiektu. Zwykle jest to ClassNotFoundException, ale jeśli masz różne wersje słoików/klas i serialVersionUID nie został zmieniony między wersjami, zostanie wygenerowany StreamCorruptedException. (Ten wyjątek może być również możliwy w przypadku konfliktu nazw klas, np .: słoik zawierający inną klasę o tej samej pełnej nazwie klasy, ale prawdopodobnie również potrzebuje tego samego serilVersionUID).

Sprawdź, czy strona klienta ma prawidłowe wersje słoików i plików klas.

+0

Nie, nie może. Ten warunek powoduje wyjątek ClassNotFoundException. – EJP

+1

Udzieliłem tej odpowiedzi, ponieważ pomyślnie naprawiłem błąd w pytaniu, instalując poprawne pliki JAR. Wierzę, że w moim przypadku miałem złe wersje (a wersja UID nie była właściwie utrzymywana), chociaż było to już dawno temu ... Może to być również możliwe z konfliktami nazw klas? @ejp Zmienię moją odpowiedź, aby wskazać wersje plików jar - uwolniłem się od odrzucenia głosowania, nie motywuje to do zapewnienia innym korzyści z doświadczeń! – drevicko

+0

Jeśli masz niedopasowanie "serialVersionUID", otrzymasz wyjątek, który wyraźnie mówi o tym, wymieniając zarówno oczekiwane, jak i aktualne "serialVersionUIDs." Nie otrzymasz 'StreamCorruptedException', który mówi' nieoczekiwany kod typu 0x00. Mis-diagnoses aren nie jest korzystne dla nikogo. – EJP

2

Jeśli ObjectInputStream jest skonstruowany tylko raz, a następnie przekazał odniesienie do innego wątku, wystarczy zamknąć dostęp tego obiektu wewnątrz bloku synchronized, aby upewnić się, że tylko jeden wątek może uzyskać dostęp do tego obiektu na raz.

Zawsze, gdy czytasz z poziomu ObjectInputStream, otwórz go wewnątrz bloku synchronized, jeśli jest on współużytkowany przez wiele wątków.


Przykładowy kod: (zrób to dla wszystkich wystąpień readObject())

... 
String nickname = null; 
synchronized (inFromClient) { 
    nickname = (String) inFromClient.readObject(); 
} 
3

Jest jeszcze jedna możliwość, że natknąłem gdzie jeśli wdrożenie rutynowych zwyczaj deserializacjia dla klasy dodając ten sposób:

private void readObject(ObjectInputStream objectInputStream) throws IOException 

następnie objectInputStream.defaultReadObject() musi zostać wywołana i zwany dalej przed odczytuje strumienia wejściowego odpowiednio zainicjowanie o bject.

Tęskniłem za tym i pomimo powrotu obiektu bez wyjątku, był to kolejny odczyt strumienia obiektu, który myląco podniósł wyjątek dotyczący nieprawidłowego kodu.

Ten link zawiera dodatkowe informacje na temat procesu: http://osdir.com/ml/java.sun.jini/2003-10/msg00204.html.

+0

To nie jest poprawne stwierdzenie problemu. Problem polega na tym, że musisz wywołać 'defaultReadObject().' Kiedy już to zrobisz, nie jest ważne, abyś przeczytał dokładną liczbę bajtów własnych rzeczy. – EJP

+0

Dodano poprawki. – user2219808

2

Ja też miałem ten wyjątek. Stało się tak, ponieważ użyłem dwóch wątków dla klasy Server i klasy Client. Użyłem jednej nici do wysyłania i odbierania przedmiotów. Wtedy było w porządku. Jest to prosty sposób na rozwiązanie problemu, jeśli nie znasz synchronized.

1

java.io.StreamCorruptedException: nieprawidłowy kod typu: 00

Niedawno wpadłem na ten problem, nie robi tego, co zrobił chociaż OP. Zrobiłem szybkie wyszukiwanie google i nie znalazłem niczego, co byłoby zbyt pomocne i ponieważ myślę, że to rozwiązałem, robię komentarz z moim rozwiązaniem.

TLDR: Nie ma wielu wątków zapisu do tego samego strumienia wyjściowego w tym samym czasie (zamiast tego na zmianę). Powoduje problemy podczas próby odczytu danych przez klienta. Rozwiązanie polega na zablokowaniu zapisu na wyjściu.

Robię coś bardzo podobnego do OP, tworząc grę wieloosobową (model klient-serwer). Mam wątek taki jak OP, który nasłuchuje ruchu. Co się działo, po mojej stronie serwera było to, że serwer miał wiele wątków, które pisały jednocześnie do strumienia klienta (nie sądziłem, że było to możliwe, gra była pół-obrotową bazą). Wątek po stronie klienta, który odbierał ruch przychodzący, rzucił ten wyjątek. Aby rozwiązać ten problem, w zasadzie położyłem blokadę na części, która napisała do strumienia klienta (po stronie serwera), więc każdy wątek po stronie serwera musiałby uzyskać blokadę przed zapisaniem do strumienia.

Powiązane problemy