Java: Client schickt, Server empfängt nur einen Teil

MAjbO

Vice Admiral Special
Mitglied seit
15.04.2002
Beiträge
540
Renomée
9
Standort
Schweiz
Hi

Ich beschäftige mich zum ersten mal mit Sockets und habe ein für mich nicht nachvollziehbares Problem.

Zum Verständnis das Konzept: Der Client schickt dem Server mehrere serialisierte Objekte. Diese sollen dann auf dem Server weiterverarbeitet werden, zu Testzwecken wird aber einfach ein String aus dem Objekt ausgegeben.
Der Fehler ist nun, dass der nicht alle vom Client verschickten Objekte auch beim Server ankommen. Als Workaround habe ich rausgefunden, dass dies nicht passiert, wenn ich nach jedem gesendeten Objekt Thread.sleep(200); aufrufe, aber das kann ja keine Lösung sein.

Vereinfachter Code (ohne Error-Handling) vom Client (messages ist eine generische ArrayList mit den LogMessage-Objekten die ich übertragen möchte).
PHP:
Socket socket = null;
socket = new Socket(server_host, port);

OutputStream oStream = socket.getOutputStream();
ObjectOutputStream ooStream = null;

for (int i=0; i<messages.size(); i++){
  ooStream = new ObjectOutputStream(oStream);
  msg = messages.get(0); 
  ooStream.writeObject(msg);
  messages.remove(0);
  System.err.println(msg.getMsg() + " übertragen");
  Thread.sleep(200);
}

Server:
PHP:
while (true)
            {
                // listen for and accept a client connection to serverSocket
                Socket sock = this.serverSocket.accept();
                InputStream iStream = sock.getInputStream();
                ObjectInputStream oiStream = new ObjectInputStream(iStream);
                try {
                    this.logMessage = (LogMessage) oiStream.readObject();
                } catch (ClassNotFoundException ex) {
                    Logger.getLogger(TCPServer.class.getName()).log(Level.SEVERE, null, ex);
                }
                System.out.println(this.logMessage.getMsg());
                iStream.close();
            }

Sieht jemand, was ich falsch mache?

Grüsse
MAjbO
 
Eines vorneweg: Bin in Java etwas aus der Übung ;) Zuerst wunder ich mich mal über das Vorgehen an sich. Auf Clientseite baust du eine Verbindung auf über die für jedes einzelne Objekt ein eigener ObjectStream gelegt wird, da würde einer reichen. Auf Serverseite rufst du in jedem Schleifenaufruf serverSocket.accept, was eigentlich heißen müsste das der Server nach jedem empfangenen Objekt die Verbindung neu aufbaut. Wundert mich ehrlich dass das überhaupt funktioniert ???

Zum eigentlichen Problem: Wenn du etwas in den ObjectStream schiebst schubst der das nicht unbedingt direkt an die Verbindung weiter sondern kann es auch zwischenspeichern. Ähnlich der OuputStream an sich. Ein flush() "spült" alles sofort raus.

Also statt:
Code:
Thread.sleep(200);
Das hier:
Code:
ooStream.flush();
oStream.flush(); //Nicht sicher ob das nötig ist

Hoffe das hilft. Würde aber wie geschrieben das Vorgehen insgesamt nochmal überdenken.
 
Wie "The G" schon geschrieben hat, ist es erstaunlich, dass das ganze überhaupt funktioniert :)
Du schreibst mehrere Objekte in eine Verbindung, liest aber auf der Server-Seite nur das erste Objekt aus.
iStream.close() verwirft dann wohl die restlichen bereits empfangenen Objekte.
Wenn du über oiStrea.readObject() loopst sollte es klappen.
Entweder auf beiden Seiten ein Objekt pro Verbindung, oder nur eine Verbindung und alle Objekte darüber versenden.
 
Zuletzt bearbeitet:
Wenn das die Ursache für das Probleme wäre dürfte aber der Sleep nicht als Workaround funktionieren. Sonst würde ich das genauso sehen. Darum wundere ich mich auch etwas ;D
 
accept() öffnet anscheinend die gleiche Verbindung nochmals, wenn ein weiteres Objekt gesendet wird.

Von da an ist es nur noch eine Timing-Sache...
 
Wäre ne schlüssige Erklärung. Der Client müsste ungefragt versuchen die getrennte Verbindung neu aufzubauen. Würde auch den Sleep-Workaround erklären, da der Client in der Zeit die Möglichkeit hat die beendete Verbindung zu registrieren und nicht das nächste Objekt in die tote Leitung pumpt. Wobei ich bei TCP eigentlich erwarten würde das da irgendwo ne Exception fliegt.
 
Erstmal danke euch beiden für die Hinweise, werde nachher mal versuchen, diese Umzusetzen.

Wenn du über oiStrea.readObject() loopst sollte es klappen.
Entweder auf beiden Seiten ein Objekt pro Verbindung, oder nur eine Verbindung und alle Objekte darüber versenden.

Wie könnte das loopen über readObject aussehen? Dafür fehlt mir eine Abbruchbedingung. Wäre es vielleicht sinnvoll, erst mal einen int mit der Anzahl der Objekte zu schicken um anschliessen genau zu wissen, wie oft man readObject aufrufen darf?
 
Du könntest statt aller Einträge einzeln einfach die ArrayList selbst schicken. Ist ja auch nur ein Objekt. Damit sparst du dir die Frage nach der Anzahl der enthaltenen Objekte. Außer Java hat Probleme mit Generics im ObjectStream *suspect*
 
AFAIR blockt readObject() bis ein neues Objekt komplett eingelesen wurde.
Mögliche Lösungen für loop:
1. while(true/_bGo) und das ganze in einen eigenen thread auslagern, den du irgendwann killst/abbrichst.
2. mit available() prüfen ob noch zusätzliche Daten vorhanden sind. Da hast du allerdings auch wieder ein Timing-Problem.
3. vielleicht bricht der read ab, wenn du die Verbindung auf der Client-Seite schliesst...
4. Wie schon vorgeschlagen ein Objekt pro Verbindung (Message oder Array)
5. Sende ein "Ende"-Objekt über das Netz um die Kommunikation zu beenden.

Gibt sicher noch andere Möglichkeiten :)
 
Ja, also gibt's bei Java keine Async-Sockets?

Das Problem sieht doch schematisch so aus:

1. Client sendet
2. Server empfängt
3. Server startet Verarbeitung
4. Client sendet
5. Server beendet Verarbeitung
6. Server ist wieder empfangsbereit

Das heißt der Server ist noch nicht soweit etwas zu empfangen, während der Client schon sendet. Bei C# kenne ich das so, dass der Server die ganze Zeit auf empfangen steht, und dann halt asynchron einen Thread für jeden empfangenen Block startet.
.
EDIT :
.

Also vielleicht kann man ja irgendwie so arbeiten (Pseudocode):

Code:
while (true)
            {
                // listen for and accept a client connection to serverSocket
                Socket sock = this.serverSocket.accept();

                new Thread(get_data(sock)); // Verbindung an Thread auslagern
            } 

void get_data(Socket sock)
{
    InputStream iStream = sock.getInputStream();
    ObjectInputStream oiStream = new ObjectInputStream(iStream);

    while ( sock.isOpen )
    {
                try {
                    this.logMessage = (LogMessage) oiStream.readObject();
                } catch (ClassNotFoundException ex) {
                    Logger.getLogger(TCPServer.class.getName()).log(Level.SEVERE, null, ex);
                }
                System.out.println(this.logMessage.getMsg());
    }
     iStream.close();
}
 
Zurück
Oben Unten