Probleme mit den ObjectStreams

Timo-Beil

Mitglied
Hallo zusammen,

Ich habe folgendes Problem mit den Java ObjectOutput- und ObjectInputstreams:

Ich baue gerade ein Client-Server Programm, was sich von der Architektur her an diesem Tutorial orientiert: http://java.seite.net/chat/sockets.html Der Server "horcht" also die ganze Zeit auf eingehende Clientverbindungen, die er dann als Thread vom Typ "Connection" in einer Liste abspeichert. Der Server kommuniziert dann mit den Clients über diese ConnectionsThreads, die jeder einen eigenen Out- und InputStream haben.

Mein Server hat nun eine Funktion, die eine Liste vom Typ Vector<Client>, zu allen Clients schicken soll. Und zwar immer wenn sich ein neuer Client verbindet. Wenn ein Client zum ersten Mal diese Liste empfängt, ist alles okay, aber wenn ein Client diese Liste zum wiederholten Mal empfängt, dann ist dieses Listenobjekt eine alte Version. Dass heißt, dieses alte Listenobjekt enthält dann nicht die aktuellen Clients, sondern nur diejenigen, die beim ersten Empfang der Liste schon enthalten waren.

Ich habe das mehrfach über Sysem.out.println und über den Debugger überprüft und es ist wirklich so, dass das Listenobjekt direkt vor dem Senden vom Server noch eine andere Größe hat als direkt nach dem Empfangen beim Client. Hier sind wichtigsten Stellen vom SourceCode, wenn die nicht ausreichen, kann ich nochmal die ganzen Klassen posten.

Der Code zum akzeptieren neuer ClientVerbindungen auf der Serverseite:
Code:
public void run()
{
	try {
		while (true){
				Socket client = server.accept();
						
				clientListe.add(new Connection(client));
						
				Logger.getLogger(this.getClass()).info(
								"Der Rechner "+client.getInetAddress().getHostAddress()+
								" verbindet sich zum Server");					
				}
			} catch (IOException ioe) {
				ioe.printStackTrace();
			}
	}



Der Code vom Senden auf der Serverseite:
Code:
	public void clientsInformieren(Vector<Client> clientListe){
		
		//Die Clients informieren
		for (Iterator<Connection> iterator = connections.iterator(); iterator.hasNext();) {
			Connection connection = iterator.next();
			
			try {
				System.out.println("Groesse der Liste: "+clientListe.size());
				connection.objectOut.writeObject(clientListe);
				connection.objectOut.flush();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

Der Code fürs Empfangen auf der Clientseite:
Code:
	public void run(){
		while(true){
			try {
				Object eingehendeDaten = objectIn.readObject();
				
				System.out.println(eingehendeDaten.getClass().getName());
				
				if( eingehendeDaten instanceof Integer){
					int code = (Integer) eingehendeDaten;
					
					if( code== DataID.codeBenutzerdatenOkay ||  
							code== DataID.codeBenutzerdatenFalsch){
						benutzerDatenCheckListener.benutzerDatenValiditaetMelden(code);
					} else{
						System.err.println("Anderer Code!");
					}
					
				} else if( eingehendeDaten instanceof Vector) {

					Vector<Client> liste = (Vector<Client>) eingehendeDaten;
					//Hier wird die Groesse der Liste ausgegeben:
					System.out.println("Liste setzen. Groesse: "+liste.size());
					clientListeListener.setListModel(new ClientListModel(liste));
				}
				
			} catch (IOException e) {
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		}
	}

Ist dieses Problem schonmal Jemandem untergekommen oder kann mir Jemand einen Tip geben, woran das liegen könnte? Das wär super!
 
Ich bin mir nicht sicher, ob der Code den Du da zeigst ausreicht um das Problem zu lokalisieren.
Gib doch außerdem (auch wenn es sich jettz banal anhört) die Object-Id und den tatsächlichen Inhalt aus. Tuhe dies sowohl auf der Server- als auch auf der Cient-Seite. Manchmal kann einem dann ein Licht aufgehen.
Im Grunde müsstest Du jedne Client über eine extra VM laufen lassen, gelle?
Ist Dein Serverabschnitt auch gleichzeitig ein Client oder läuft der auch grundsätzlich separat?

dann noch eine Frage bezüglich des Problems mit einer alten Liste.
Verstehe ich Dich richtig, dass Der 2. Client niemandne in der Liste bekommt, und der 3 nur einen der 4. nur 3 etc?
 
Zuletzt bearbeitet:
Hallo,

ich habe mal mein Programm auf das Minimum heruntergebrochen, um mein Problem verständlich reproduzieren zu können. Der nachfolgende Code hier ist quasi die Basis meines Programms und der Fehler tritt in genau dem selben Muster auf.

Um den Fehler zu reproduzieren started man den Server. Der nimmt dann die ClientSockets an und fügt gleichzeitig ein Element in die Integer-Liste ein. Diese wird dann an alle Clients in der Connectionsliste versendet.

Jetzt startet man hintereinander zwei Clients. Der erste Client müsste also zuerst eine Liste mit einem Element bekommen und nachdem sich der zweite Client verbunden hat, müsste er eine Liste mit zwei Elementen bekommen. Aber hier liegt der Fehler! Er bekommt nämlich zweimal die selbe Liste! Mit genau demselben Hashwert und dem selben Inhalt!

ich weiß da leider nicht weiter. Hab ich vielleicht was bei dem Erstellen der Sockets falsch gemacht?

Hier ist also der Code:
Zuerst der Server:
Code:
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Iterator;
import java.util.Vector;

public class Server {
	private ServerSocket server = null;
	private Vector<Connection> clientListe = new Vector<Connection>();
	private Vector<Integer> liste = new Vector<Integer>();

	public Server(){
		
		try {
			server = new ServerSocket(1299);
			
			int i = 1;
			while(true){
				Socket client = server.accept();
				clientListe.add(new Connection(client));
				liste.add(i);
				i++;
				
				for (Iterator<Connection> iterator = clientListe.iterator(); iterator
						.hasNext();) {
					Connection connection = iterator.next();
					connection.listeSchicken(liste);
				}
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
		
	public static void main(String[] args) {
		new Server();
	}
}

Die ConnectionsKlasse:
Code:
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Vector;

public class Connection
{
	private ObjectOutputStream objectOut;
	
	public Connection(Socket clientSocket)
	{
		try
		{
			objectOut = new ObjectOutputStream(clientSocket.getOutputStream());
		}catch(IOException e)
		{
			e.printStackTrace();
		}
	}
	
	public void listeSchicken(Vector<Integer> liste){
		try{
			objectOut.writeObject(liste);
			objectOut.flush();
		} catch (Exception e){
			e.printStackTrace();
		}
	}
}

Und der Client:
Code:
import java.io.ObjectInputStream;
import java.net.Socket;

public class Client {
	
	private Socket client;
	private ObjectInputStream objectIn;

	private Client(){
		try {
			client = new Socket("localhost", 1299);
			System.out.println("connected");
			
			objectIn = new ObjectInputStream(client.getInputStream());
			
		} catch (Exception e){
			e.printStackTrace();
			System.exit(1);
		}
		
		while(true){
			try{
				Object eingehendeDaten = objectIn.readObject();
				System.out.println(eingehendeDaten.toString());
			} catch (Exception e){
				e.printStackTrace();
				System.exit(0);
			}
		}
	}
	
	public static void main(String[] args){
		new Client();
	}
}
 
Ich habe den Stream zurückgesetzt, dann geht es

Java:
public class Server {
	
	private Vector<Connection> clientListe = new Vector<Connection>();
	private Vector<Integer> liste = new Vector<Integer>();
	
	
	public Server() {
		try {
			ServerSocket serverSocket = new ServerSocket(1299);
			int counter = 0;
			
			while(true){
				Socket socket = serverSocket.accept();
				liste.add(new Integer(counter ++));
				// neuen Clienthandler in eigenem Thread starten.
				new Connection(socket).start();
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public synchronized void broadcast() {
		for(Connection connection : clientListe){
			connection.send();
		}
	}
	class Connection extends Thread {
		private Socket socket = null;
		private ObjectOutputStream objectOutputStream = null;
		
		public Connection(Socket socket) throws IOException{
			this.socket = socket;
			this.objectOutputStream = new ObjectOutputStream(new DataOutputStream(socket.getOutputStream()));
			//Connectioninstance in die Handlerliste eintragen
			clientListe.add(this);
			
			
		}
		
		public void run() {
			
				broadcast();
				
			
			
		}
		
		public void send() {
			try {
				objectOutputStream.writeObject(liste);
				objectOutputStream.flush();
				objectOutputStream.reset();
				
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		
	}
	
	public static void main(String[] args) {
		new Server();
	}
}
 
Oh Mann, es war tatsächlich nur die Methode reset(), die gefehlt hat!! Vielen Dank für den Tipp, du hast mir einiges an Frust und Arbeit erspart :)

Deine Serverklasse werde ich mir morgen mal ankucken.

Vielen Dank nochmal, machs gut und halt die Ohren steif :)

Timo
 
Hi,

das Problem mit der Acceptmethode ist, das sie blockiert. Das heißt, wärend Du auf neue Clients wartest, kannst Du keine Anfragen von bestehenden Clients verarbeiten. Das fällt bei Deiner Lösung nicht auf, weil jeder Client nur beim Verbindungsaufbau etwas tut.

Die Einfachste Lösung dafür ist Nebenläufigkeit. Jede ClientConnection läuft in einem eigenen Thread, dadurch kann der HauptThread mit Accept ruhig blockieren, die Clients können weiter kommunizieren.

Hier ein kleiner quick and dirty Chatserver für Telnetclients zum rumspielen:

Java:
package de.tutorials;



import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

/**
 * 
 * Chatserver für Telnetclients
 * starte Telnet mit localhost oder ip und Port 9000
 * 
 * @author Jo
 *
 */
public class ChatServer extends Thread {

	private static List<ChatServer> clients = new ArrayList<ChatServer>();
	private Socket socket;
	private BufferedReader reader ;
	private PrintWriter out;
	
	ChatServer(Socket socket) throws IOException{
		this.socket = socket;
		reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
		out = new PrintWriter(socket.getOutputStream());
		clients.add(this);
	}
	
	
	@Override
	public void run() {
		String nickname = null;
		String eingabe = null;
		try {
			
			send ("Chatserver 1.0");
			send ("Wie ist Dein Nickname?");
			nickname = reader.readLine();
			broadcast(nickname + " erscheint aus dem Nichts....");
			while ((eingabe = reader.readLine()) != null) {
				if (eingabe.equalsIgnoreCase("bye")) break;
				broadcast(nickname + ": " + eingabe);
			}
		} catch (Exception e) {
			
		}
		clients.remove(this);
		broadcast(nickname + " verschwindet in einer Rauchwolke!");
		if (socket.isConnected())
			try {
				socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		
	}
	
	private void send(String message) {
		out.println(message);
		out.flush();
	}


	/**
	 * @param args
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException {
		
		ServerSocket serverSocket = new ServerSocket(9000);
		System.out.println("Socket an Port 9000 etabliert!");
		
		while (true) {
			System.out.print("Warte auf eingehenden Verbindungen...");
			Socket socket = serverSocket.accept();
			System.out.println("ok!");
			System.out.println(socket + " erkannt");
			System.out.println("Starte Handler");
			new ChatServer(socket).start();
		}

	}
	
	public static synchronized void broadcast(String message) {
		for(ChatServer client: clients)
			client.send(message);
	}

}
 
Zurück