Witam.
Piszę prosty komunikator internetowy. Serwer odpala jeden wątek dla każdego połączenia, w którym obsługuje zarówno wysyłanie, jak i odbieranie wiadomości. Zależnie od treści odpowiedniego pola serializowanej klasy Message decyduje, co dalej począć z odebraną od klienta wiadomością. Natomiast klient w momencie zalogowania na serwer uruchamia wątek główny, który odpowiada za tworzenie dwóch wątków: odbierającego i wysyłającego, automatycznie wysyła na serwer Message z danymi klienta do zalogowania, a później pośredniczy w przekazywaniu temu ostatniemu kolejnych wiadomości z GUI klienta (ConcurrentLinkedQueue).
Mam problem ze skoordynowaniem działania obiektów klas ObjectOutputStream i ObjectInputStream. Wyrzuca mi błąd java.io.StreamCorruptedException: invalid type code: AC ze wskazaniem na metody readObject() po obu stronach socketa. Ten sam błąd wyskakuje przy transmisji zwykłego Stringa, więc tu raczej nie leży przyczyna...
Podejrzewam, że wynika to z faktu, iż wątek odpowiedzialny za wysyłkę iteruje się wielokrotnie próbując nadpisywać outputStream, podczas gdy wątek nasłuchujący bezczynnie czeka z inputStreamem na nadejście obiektu. Co ciekawe, za pierwszym razem wszystko jest w porządku: klient poprawnie zalogowuje się na serwerze, a ten umieszcza jego dane w odpowiednim HashSecie i odsyła klientowi powitalną wiadomość, która wyświetla się w okienku tego ostatniego. Próbowałem inicjalizować output i input w różnych miejscach, ale nigdy nie dało to pożądanego rezultatu. Co zatem robię źle?
Metoda run wątka wysyłającego po stronie klienta:
public void run() {
try {
this.output = new ObjectOutputStream(this.socket.getOutputStream());
this.output.flush();
while(true) {
if(this.messageQueue.isEmpty()) continue;
this.message = this.messageQueue.poll();
if(this.message == null) {
continue;
} else {
this.output.writeObject(this.message);
this.output.flush();
this.message = null;
}
}
} catch(IOException ioex) {
ioex.printStackTrace();
}
}
Metoda run wątka odbierającego po stronie klienta. W ramach wyjaśnienia: wiadomość odbierana przez klienta może mieć nagłówek "Online" - to oznacza, że zawiera ona obiekt typu HashSet<String> z listą zalogowanych klientów. W przeciwnym wypadku jest to zwykła wiadomość, która zostaje wypisana w okienku GUI:
public void run() {
try {
this.input = new ObjectInputStream(this.socket.getInputStream());
while(true) {
this.message = (Message) input.readObject();
if(this.message.getText().equals("Online")) {
this.usersOnline = null;
this.usersOnline = this.message.getUsersOnline();
this.client.usersOnlineUpdate(this.usersOnline);
} else {
this.client.setText(this.message.getName() + ": "
+ this.message.getText());
}
this.message = null;
}
} catch(IOException ioex) {
ioex.printStackTrace();
} catch(ClassNotFoundException cnfex) {
cnfex.printStackTrace();
}
}
Metoda run wątka po stronie serwera:
public void run() {
try {
this.input = new ObjectInputStream(socket.getInputStream());
this.output = new ObjectOutputStream(socket.getOutputStream());
while(true) {
this.receivedMessage = (Message) this.input.readObject();
this.name = this.receivedMessage.getName();
if(this.receivedMessage.getInterlocutor().equals("Server")) {
if(this.receivedMessage.getText().equals("whois")) {
onlineUsers();
continue;
}
if(this.receivedMessage.getText().equals("quits")) {
logOutUser();
}
if(this.receivedMessage.getText().equals("connect")) {
System.out.println("run(if connect!)");
logInUser();
}
}
}
} catch(ClassNotFoundException cnfex) {
this.server.setText("Błąd: " + cnfex);
} catch(IOException ioex) {
this.server.setText("Błąd: " + ioex);
}
}
private void logInUser() {
// dodawanie logującego się użytkownika do listy zalogowanych
if(!this.server.getUsersOnline().contains(this.receivedMessage.getName())) {
this.server.addUser(this.receivedMessage.getName());
}
this.name = this.receivedMessage.getName();
// wysyłanie wiadomości powitalnej
this.sentMessage = new Message("Serwer", "Witaj w RS Communicator!");
this.server.setText(this.sentMessage.getName() + ": " +
this.sentMessage.getText());
this.send(sentMessage);
}
private void onlineUsers() {
// wysyłanie klientowi listy zalogowanych użytkowników
this.sentMessage = new Message("Server", "Online",
new HashSet <String>(this.server.getUsersOnline()));
this.send(sentMessage);
}
private void send(Message message) {
try {
this.output.writeObject((Message) message);
this.output.flush();
} catch(IOException ioex) {
ioex.printStackTrace();
}
}
Byłbym wdzięczny za wszelkie porady.