# Übertragung via Sockets



## Bohne (3. Mrz 2006)

Ich arbeite mich gerade in das Thema Sockets ein und habe eine wohl triviale Frage, die ich leider nicht lösen kann: Es geht um eine Server-Client App, bei der der Client nacheinander zwei Zahlen an den Server schickt und dieser sie multipliziert und das Ergebnis zurückschickt. Code wie folgt:


```
import java.net.*;
import java.io.*;

public class Server
{
	public static void main( String args[] ) throws IOException
	  {
	    ServerSocket server = new ServerSocket( 3141 );

	    while ( true )
	    {
	      Socket client = server.accept();

	      InputStream  in  = client.getInputStream();
	      OutputStream out = client.getOutputStream();

	      int start = in.read();
	      int end = in.read();

	      int result = start * end;
	      System.out.println(result);
	      out.write(result);

	      client.close();
	    }
	  }
}
```


```
import java.net.*;
import java.io.*;

public class Client {
	public static void main( String args[] ) throws IOException {
		Socket server = new Socket ( "localhost", 3141 );
	
	    InputStream in = server.getInputStream();
	    OutputStream out = server.getOutputStream();
	
	    out.write( 99 );
	    out.write( 9 );
	
	    int result = in.read();
	    System.out.println( result );
	
	    server.close();
	}
}
```

Problem: Wenn ich das Ergebnis von 4*9 berechnen lasse funktioniert's, bei 99*9 jedoch nicht. Es scheint bei der Übertragung was nicht zu klappen, da der Server das richtige Ergebnis ausgibt. Auf der Client-Seite printet er jedoch 123 - da kann doch was nicht stimmen?!

Danke!


----------



## sebastian4gold (3. Mrz 2006)

Lass mich raten: Java Insel-Buch?

Das Problem besteht darin, das nur Bytes übertragen werden, und so das Ergebis nicht größer als 127 sein darf. (Steht im Buch sogar unten drunter :_) )

Problemlösung:
Client: String ss = "" + 9 + "," + "99"; write(ss.toString().getBytes())
und Server:StringBuffer s = new StringBuffer(); s.append( read() );

Dann hast du auf dem Server einen String "9,99", kannst du wieder trennen (StringTokenizer Klasse), dann Ergebnis als String zurückschicken.

Gruß, hoffe geholfen zu haben,
Sebastian


----------



## Guest (3. Mrz 2006)

Bohne hat gesagt.:
			
		

> Problem: Wenn ich das Ergebnis von 4*9 berechnen lasse funktioniert's, bei 99*9 jedoch nicht. Es scheint bei der Übertragung was nicht zu klappen, da der Server das richtige Ergebnis ausgibt. Auf der Client-Seite printet er jedoch 123 - da kann doch was nicht stimmen?!
> 
> Danke!



Wieso nimmst du DataOutputStream bzw. DataInputStream nicht? Dann könntest Du Zahlen direkt schreiben und musst dich mit Bytes nicht herumschlagen?


----------



## Beni (3. Mrz 2006)

read & write lesen/schreiben nur ein byte (von 4 beim int), blabla, Forensuche, API, blabla, Binäre Operationen, DataInput/OutputStream.


----------



## Bohne (4. Mrz 2006)

Hab es jetzt mit DataInput-/Output- Streams versucht, und es funktioniert!


```
Socket client = server.accept();

InputStream  in  = client.getInputStream();
OutputStream out = client.getOutputStream();
	      
try {
	DataInputStream data = new DataInputStream(in);
	DataOutputStream result = new DataOutputStream(out);
	int start = data.readInt();
	int end = data.readInt();
	int ergebnis = start * end;
	result.writeInt(ergebnis);
} catch (IOException e) {
	System.err.println(e);
}
client.close();
```


```
Socket server = new Socket ("localhost", 3141 );
	
InputStream in = server.getInputStream();
OutputStream out = server.getOutputStream();
		
try {
	DataInputStream result = new DataInputStream(in);
	DataOutputStream data = new DataOutputStream(out);
	data.writeInt(99);
	data.writeInt(9);
	int ergebnis = result.readInt();
	System.out.println(ergebnis);
} catch (IOException e) {
	System.err.println(e);
}

server.close();
```

Allerdings versuche ich noch vergeblich einen BufferedInput-/Output-Stream einzubauen: Die Write-Funktion wird zwar ausgeführt (hab ich ausprobiert, indem ich danach was auf die Shell ausgeben lasse), allerdings liest der Server die Daten irgendwie nicht ein. Relevanter Code (hier für die Client-Klasse) wie folgt:


```
InputStream in = server.getInputStream();
		OutputStream out = server.getOutputStream();
		
		BufferedInputStream bufin = new BufferedInputStream(in);
		BufferedOutputStream bufout = new BufferedOutputStream(out);
		
		try {
			DataInputStream result = new DataInputStream(bufin);
			DataOutputStream data = new DataOutputStream(bufout);
			data.writeInt(99);
			data.writeInt(9);
			int ergebnis = result.readInt();
		}
		[...]
```


----------



## Beni (4. Mrz 2006)

Beim BufferedOutputStream musst du noch "flush()" aufrufen, damit du sicher sein kannst, dass auch alles im Buffer gesendet wird. Sonst wartet der Stream, ob nicht noch mehr kommt...


----------



## Bohne (4. Mrz 2006)

Cool, funktioniert. Ich hatte das ja schon mal irgendwo gehört, allerdings hab ich's dann direkt auf bufout angewendet und es hat nicht funktioniert, sodass ich gedacht habe, ich hab mich getäuscht. Aber mit data.flush() klappt es prima, danke!


----------



## Bohne (6. Mrz 2006)

Nachdem es mit Data-Streams funktioniert hat, habe ich es auch noch mit Object-Streams probiert, allerdings hängt das Programm wieder bei der ersten write-Funktion, obwohl ich flush() verwende. Am Code hat sich im Prinzip nichts großartig verändert:

Server:

```
InputStream  in  = client.getInputStream();
OutputStream out = client.getOutputStream();
  
ObjectInputStream data = new ObjectInputStream(in);
ObjectOutputStream result = new ObjectOutputStream(out);

Object o = data.readObject();

result.writeObject(o);
result.flush();
```


Client:

```
InputStream in = server.getInputStream();
OutputStream out = server.getOutputStream();
	
ObjectInputStream result = new ObjectInputStream(in);
ObjectOutputStream data = new ObjectOutputStream(out);
		
data.writeObject("test");
data.flush();

Object ret = result.readObject();
System.out.println(ret);
```


----------



## Bohne (6. Mrz 2006)

Also es ist zum aus der Haut fahren :x Folgender Code funktioniert perfekt:

Server:

```
public static void main(String[] args) throws IOException, ClassNotFoundException {
		ServerSocket server = new ServerSocket(3141);

		while (true) {
			Socket client = server.accept();

			ObjectInputStream in = new ObjectInputStream(client.getInputStream());
			ObjectOutputStream out = new ObjectOutputStream(client.getOutputStream());
						
			Object o = in.readObject();
			System.out.println(o.toString());
		
			out.writeObject(o);
			out.flush();
			
			out.close();
			in.close();			
			client.close();
		}
	}
```

Client:

```
public static void main(String[] args) throws IOException, ClassNotFoundException {
		Socket server = new Socket ("localhost", 3141 );
	
		ObjectOutputStream out = new ObjectOutputStream(server.getOutputStream());
		ObjectInputStream in = new ObjectInputStream(server.getInputStream());

		out.writeObject(new Date());
		out.flush();
		Object o = in.readObject();
		
		System.out.println(o.toString());
			
		in.close();
		out.close();		
		server.close();
	}
```

Jetzt *vertauscht* aber mal die Zeilen 8/9 (Server) _oder_ 5/6 (Client), sodass das sowohl bei Server als auch Client die Input/Output-Streams in gleicher Reihenfolge angelegt werden (also erst in, dann out, oder erst out, dann in).

*Was denkt ihr passiert?* - Funktioniert das Programm dann noch?

Ich hab keine Ahnung, was passiert, jedenfalls hängt das Programm dann beim Anlegen des zweiten Streams (egal, ob jetzt in oder out an zweiter Stelle steht), alle Befehle danach werden nicht ausgeführt.

Mir ist dieses Verhalten äußerst schleierhaft und ich habe auch nirgends etwas dazu gefunden, aber vielleicht gibts hier ja ein paar Gurus, die mehr dazu wissen. Evtl ist es ja ein Bug in der jvm? Ich benutze 1.5.0_06 für Linux i386.


----------



## igor99 (6. Mrz 2006)

Ich kann dir nicht sicher sagen, woran es liegt. Ich habe kürzlich auch eine Socket-Applikation programmiert, wobei ich Objekte übertragen habe. Habe ähnliche Frage gestellt und den Tipp bekommen, dass 

1. der Sender zuerst einen ObjectOutputStream und dann ObjectInputStream anlegen muss, wöhrend  
2. der Empfänger die beiden Streams in umgekehrten Reihenfolge erzeuen muss (Input und erst dann Output).

Angeblich kann der Empfänger erst dann etwas lesen, wenn etwas geschreiben wurde. Aus diesem Grund muss man auf der Senderseite zuerst einen Output-Stream anlegen. Denn, nur so wird der Kreis (in einer Richtung) geschlossen.

Ich habe es so gemacht und es hat tatsächlich funktioniert. Zufall oder nicht, kann ich nicht sagen. Falls ich den Link zu diesem Thread finden sollte, werde ich es angeben.


----------



## igor99 (6. Mrz 2006)

Hier die URL:

http://www.java-forum.org/de/viewtopic.php?t=26731&highlight=objectoutputstream


----------



## Bohne (6. Mrz 2006)

Hm, danke. Ich denk damit man die Sache checkt muss man wohl genau wissen, wie das in den jeweiligen Klassen gelöst ist. So quasi als Anwender bleibt einem davon ja (der Kapselung sei Dank!) einiges verborgen.

Das sollte man irgendwo mal festhalten, in der JavaInsel und dem Handbuch der Java-Programmierung hab ich leider den Vermerk nicht finden können.


----------



## Bohne (7. Mrz 2006)

Ich hab mir das über Nacht nochmal durch den Kopf gehen lassen und jetzt ist es eigentlich völlig logisch:

Zu einer Verbindung gehören immer zwei - d.h. wenn der Client eine Ausgabe an den Server öffnet, muss der Server einen Kanal öffnen, in dem er die Daten einliest. Geschieht das nicht, wartet der jeweils andere Teilnehmer darauf, dass sein Anliegen akzeptiert wird, um sicherzustellen, dass die gesendeten Daten nicht einfach im Nirvana verschwinden.

Eigentlich glasklar, aber wenn man erstmal nur Code abtippt ohne sich Gedanken zu machen, dann kommt man wohl nur durch Ausprobieren drauf.


----------

