Zum Inhalt springen

TCP-Dateiübertragung


Empfohlene Beiträge

Geschrieben

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 ...

Gast runtimeterror
Geschrieben

Ü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.

Geschrieben

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);
}

Gast runtimeterror
Geschrieben

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.

Geschrieben

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?

Gast runtimeterror
Geschrieben

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.

Geschrieben (bearbeitet)

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

Dein Kommentar

Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.

Gast
Auf dieses Thema antworten...

×   Du hast formatierten Text eingefügt.   Formatierung wiederherstellen

  Nur 75 Emojis sind erlaubt.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Editor leeren

×   Du kannst Bilder nicht direkt einfügen. Lade Bilder hoch oder lade sie von einer URL.

Fachinformatiker.de, 2024 by SE Internet Services

fidelogo_small.png

Schicke uns eine Nachricht!

Fachinformatiker.de ist die größte IT-Community
rund um Ausbildung, Job, Weiterbildung für IT-Fachkräfte.

Fachinformatiker.de App

Download on the App Store
Get it on Google Play

Kontakt

Hier werben?
Oder sende eine E-Mail an

Social media u. feeds

Jobboard für Fachinformatiker und IT-Fachkräfte

×
×
  • Neu erstellen...