Zum Inhalt springen
View in the app

A better way to browse. Learn more.

Fachinformatiker.de

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

TCP-Dateiübertragung

Empfohlene Antworten

Veröffentlicht

Hallochen,

ich versuche grade eine Textdatei von einem client auf einen server zu schicken und diese dann vom server ausgeben zu lassen. Das hat auch soweit erstmal funktioniert, nur dass der Server die Umlaute nicht darstellen konnte. Ich hab mir den Abschnitt über Netzwerkprogrammierung in Java im Buch von Addison Wesley fertig durchgelesen und ausprogrammiert, leider fande ich das für so ein gewaltiges Thema recht wenig und ich habe bisher auch noch keine bessere Quelle / Buch gefunden, wo mehr darüber erklärt wird ( udp wurde zb ganz weggelassen :/ ) Jedenfalls habe ich nach einigen Beispielen gegoogelt, aber das hat mich nur zurückgeworfen, sodass ich dann beim Server nur noch eine Zeile bei der Ausgabe des Textes habe. Kann mir vielleicht jemand nochmal die Datenübertragung etwas näher bringen und vielleicht auch den Unterschied zu UDP? Erstmal hier meine beiden Klassen:

TCP_Client


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

public class TCP_Client {

public static void main(String[] args) throws IOException {

BufferedReader br = new BufferedReader( new FileReader( "alice.txt" ) );


Socket clientSocket = new Socket("localhost",3000);
OutputStream out = clientSocket.getOutputStream();

//DataOutputStream outToServer=new DataOutputStream(clientSocket.getOutputStream());

//BufferedReader inFromServer = new BufferedReader( new InputStreamReader( clientSocket.getInputStream()));

LineNumberReader lnr = new LineNumberReader( br );
String line;

while( ( line = lnr.readLine() ) != null ){
out.write(line.getBytes());
System.out.println( line );
}
out.write('\r');
out.write('\n');

clientSocket.close();
br.close();
//sentence = inFromUser.readLine(); // blockiert
/*byte[] buffer = new byte[1024];
while(fileIn.available() > 0){
outToServer.write(buffer,0,fileIn.read(buffer));
}*/


}

}
[/PHP]

TCP_Server

[PHP]
import java.io.*;
import java.net.*;


public class TCP_Server {

public static void main(String[] args)throws Exception {

String clientSentence;
//String capitalizedSentence;
ServerSocket welcomeSocket = new ServerSocket(3000);

while(true){
Socket connectionSocket = welcomeSocket.accept(); // wartet auf Verbindung vom Client
// danach geht es weiter

InputStream in = connectionSocket.getInputStream();

// Eingangs-Stream vom Socket holen
//BufferedReader inFromClient = new BufferedReader( new InputStreamReader( connectionSocket.getInputStream()));

// Stream für Ausgang aufbauen
//DataOutputStream outToClient = new DataOutputStream( connectionSocket.getOutputStream() );

byte[] buffer = new byte[1024]; // byte zur Übertragung von Daten
int c;

while( connectionSocket.isConnected() ){
c = in.read(buffer);
if(c==-1)
break;
System.out.write( buffer,0,c );
}


/*
while( (len = in.read(buffer) ) != -1) {
System.out.write(buffer,0,len);
}


while( true ){
clientSentence = in.readLine();
System.out.println(clientSentence);
}
*/

in.close();
connectionSocket.close();

}

}

}

Wäre super, wenn mir jemand erklären könnte warum keine der auskommentierten Varianten beim Server funktioniert.

Wenn ich beispielsweise

System.out.println( buffer.toString() );

beim Server verwende, dann bekomme ich ne ganze Menge davon:

[B@213214d1

[B@213214d1

[B@213214d1

[B@213214d1

[B@213214d1

[B@213214d1

[B@213214d1

Ist mir schon etwas schleierhaft ...

Über Netzwerke werden immer Bytes übertragen und keine Zeichen. Bei der Umwandlung von Zeichenketten (String) in Byte-Folgen (byte[]) und umgekehrt muss immer ein Charset angegeben werden, welches die Regeln der Umwandlung beschreibt. Die von dir verwendeten Methoden machen dies implizit immer mit dem Default-Charset der jeweiligen JVM - welches je nach Mondstand ein anderes sein kann. Verwenden Server und Client unterschiedliche Charsets, so macht sicht dies meist bei den Umlauten bemerkbar.

Statt:

BufferedReader br = new BufferedReader( new FileReader( "alice.txt" ) );

Besser:

BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("alice.txt"), Charset.forName("UTF-8")));

Oder:

private static final CHARSET = Charset.forName("UTF-8")

BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("alice.txt"), CHARSET));

Oder (ab Java 7):

BufferedReader br = Files.newBufferedReader(new File("alice.txt").toPath(), CHARSET)
Die toString-Methode gibt dir für byte-Arrays ein Objekt-Handle aus. Java kann nicht wissen, dass die darin enthaltenen Bytes lesbaren Text darstellen sollen.
Statt:

System.out.println( buffer.toString() );

Meinst du vermutlich:

System.out.println( new String(buffer) );

Oder besser:

System.out.println( new String(buffer, 0, c, CHARSET) );

> Wäre super, wenn mir jemand erklären könnte warum keine der auskommentierten Varianten beim Server funktioniert.

Hier würde ich dich bitten genauer zu beschreiben, welche der zahllosen auskommentierten Bereiche welche Probleme verursachen.

  • Autor

Danke dir, die Zeile


System.out.println( new String(buffer) );
[/PHP]

ist ne Magie, die ich bisher auch noch nicht kannte. Das funktioiert auch auf Serverseite ;-)

Allerdings hab ich den Client jetzt so, da mir gesagt wurde, dass es besser sei die Bytes direkt zu lesen und zu schicken, also ohne BufferedReder etc, weil sonst Konvertierungen vorgenommen werden, die Ergebnis verfälschen könnten? Allerdings leuchtet mir das nicht ganz ein, da der bufferedReader doch nur bytes puffert bevor er sie rausschiebt, oder?

[PHP]
FileInputStream fis = new FileInputStream("alice.txt");

Socket clientSocket = new Socket("localhost",3000);
OutputStream out = clientSocket.getOutputStream();

byte[] buffer = new byte[1024];
while( fis.read( buffer ) != -1 ){
out.write(buffer);
}

Falls die Forderung ist, die Textdatei unverändert zu übertragen (also Binär identisch), dann darfst du tatsächlich keinen Reader oder Writer einsetzen. Diese arbeiten ausschließlich auf Zeichen-Ebene (also auf interpretierten Bytes).

Byte <-> Zeichen

InputStream <-> Reader

OutputStream <-> Writer

BufferedInputStream <-> BufferedReader

BufferedOutputStream <-> BufferedWriter

Ein kurzer Blick in die Java-Docs zu diesen Klassen dürfte auch ein wenig Klarheit bringen. Wenn es nach mir ginge dürfte es keine Methode geben, die die Umwandlung zwischen byte[] und String erlaubt, ohne dass ein explizites Charset angegeben wurde. Das machen einfach zu viele falsch.

  • Autor

Byte <-> Zeichen

InputStream <-> Reader

OutputStream <-> Writer

BufferedInputStream <-> BufferedReader

BufferedOutputStream <-> BufferedWriter

Danke! Einleuchtend auf einem Blick, kommt gleich an mein Whiteboard ;-)

Ich wusste nicht, dass die Datei dadurch beim übertragen verändert werden kann. Ist das etwa gewollt?

Es werde nur Bytes übertragen - dort findet keine Umwandlung statt.

Die Umwandlung ist dir beim Auslesen der Datei passiert.

Was du gemacht hast:

Bytes (aus Datei) -> Reader -> String -> getBytes -> Bytes -> Übertragung -> Bytes -> Reader -> String

Was besser ist:

Bytes (aus Datei) -> Übertragung -> Bytes -> Reader -> String

Immer wenn Bytes in String umgewandelt werden (oder umgekehrt) finden Veränderungen statt. Das liegt daran, dass sich durch Bytes alles übertragen lässt, durch Text jedoch nicht.

Versuche immer alles was schon als Bytes vorliegt so lange zu lassen, wie nötig.

  • Autor

Gut zu wissen, aber mal was anderes. Unabhängig davon ob ich nun tcp oder udp nehme; angenommen ich packe Daten, die nicht über den header mitgesendet werden können mit in mein erstes Paket bzw. in jedes mit rein. Sei es beispielsweise ein Dateiname. Natürlich kann ich auch das erste Paket nur mit dem Namen ausstatten und losschicken, aber wenn ich Daten mit im Paket habe und diese dann beim auslesen trennen möchte, wie könnte man dann vorgehen? Mein erster Gedanke wäre einfach einen int am Anfang des Paketes zu stecken für die Anzahl Zeichen des Strings, dann die Anzahl Zeichen für den Name der Datei auslesen und den Rest normal als Daten behandeln. Würde das funktionieren? Wenn ich mir ein byte in Größe eines int erstelle und erst damit receive aufrufe, was passiert bei einem weiteren Aufruf von receive? Steht der Zeiger dann wieder am Anfang oder nach dem int? Die Zusammensetzung der Pakete bzw Header ist mir noch etwas schleierhaft. Mit Funktionen wie getAddress() oder getPort() hole ich mir Daten aus dem Header, alles weitere muss ich also in den Datenteil packen?

Gruß

Bearbeitet von Kadaj

Archiv

Dieses Thema wurde archiviert und kann nicht mehr beantwortet werden.

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.