Kann nicht mehr als 8192 Byte über Sockets senden

noxum

Mitglied
Hallo liebes Forum,

ich hab mich mal seit längeren wieder mit Sockets beschäftig. Allerdings habe ich jetzt ein kleines Problem.
Und zwar sende ich vom Client eine Datei welche 242774 Byte groß ist, beim Server kommen aber nur 8192 Byte an weshalb die Datei danach Fehlerhaft ist.

Jetzt habe ich über Google schon so viel erfahren, dass die Päckchen welche gesendet werden wohl standartmäßig 8192 Byte groß sind.

Jetzt ist meine Frage, kann ich auf recht einfachen Weg größere Päckchen versenden oder gibt es da vllt. noch eine andere Möglichkeit?

Ich könnte die Datei eigentlich ja auch in kleinere Teilchen aufteilen und diese kleinen Teilchen dann einzeln senden, aber würde gerne wissen ob es da einen einfacheren Weg gibt.

Danke für alle Antworten.
MFG Noxum
 
Danke schonmal euch beiden, alles eigentlich recht gut verständlich.
Allerdings müssen wir in der Schule eine leicht abgeänderte Socketklasse verwenden, da ich diese auch in der Prüfung benutzen muss, kann ich leider nicht die Standartklasse benutzen.

Da ich vergessen habe den Code zu posten mach ich dies nun.



Die Datei welche empfängt:
Java:
import java.io.*;

public class Empfaenger
{
	public static void main(String args[])
	{
		try
		{
			ServerSocket server = new ServerSocket(4455);
			Socket s;

			s = server.accept();
			System.out.println("Acceptet!");
			Thread.sleep(500);

			int byteLength = s.dataAvailable();
			System.out.println(byteLength);
			byte[] data = new byte[byteLength];
			s.read(data,byteLength);

			server.close();

			String NewDatName = "versuch.jpg";
			FileOutputStream fos = new FileOutputStream(NewDatName);
			fos.write(data);




		}
		catch(Exception e)
		{
			System.out.println("Es ist ein Fehler beim Empfangen der Datei aufgetreten\n\nBeschreibung:\n\n"+e.toString()+"\n\n\n");
		}

	}
}


Die Datei welche sendet:
Java:
import java.io.*;

public class Main
{
	public static void main(String agrs[])
	{
		try
		{
			File myFile = new File("versuch.jpg");
			FileInputStream fis = new FileInputStream(myFile);

			byte[] data = new byte[(int)myFile.length()];
			fis.read(data);

			long len = myFile.length();
			System.out.println(len+"\n\n");
			Socket s = new Socket("localhost",4455);
			if(s.connect())
			{
				s.write(data,(int)len);
			}
			fis.close();
			s.close();



		}
		catch(Exception e)
		{
			System.out.println("Es ist ein Fehler beim Senden der Datei aufgetreten\n\nBeschreibung:\n\n"+e.toString()+"\n\n\n");
		}
	}
}



Damit ihr auch versteht was ich da oben versuche hier nochmal die etwas andere Socketklasse:

Java:
//package socketio;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.BufferedReader;

/**
 * <b>Ein-/Ausgabe über Sockets</b>. <br />
 *  Ein Objekt der Klasse Socket kapselt eine Socket-Schnittstelle.
 *  Festlegung der Socket-Parameter kann nur bei der Objekt-Erzeugung
 *  vorgenommen werden.<br />
 *  <br />
 *  Das Socket-Objekt kann sich mit einem Server-Socket.
 *  Ob die Verbindungsanforderung erfolgreich war, kann durch den Rückgabewert
 *  der Methode {@link Socket#connect connect()} festgestellt werden (true, wenn
 *  die Verbindung akzeptiert wurde).<br />
 *  <br />
 *  Das Socket-Objekt gestattet im Zustand &quot;verbunden&quot; das Lesen bzw.
 *  Schreiben von einzelnen Bytes, Byte-Arrays oder Strings von der bzw. auf
 *  die Socket-Schnittstelle.<br />
 *  <br />
 *  Copyright 2006, Michael Zimmer<br />
 *  <br />
 *  @author   Michael Zimmer
 *  @version  1.1 (Mo, 13.03.2006, 17:42 MEZ)
 *  @see ServerSocket
 */
public class Socket {
  	/** Der Hosname des Servers. */
	private String hostname;
  	/** Der Port, auf dem der Server-Socket horcht. */
	private int port;
  	/** Der Socket. */
	private java.net.Socket socket;
    /** Ein BufferdReader zum Lesen von Strings */
	private BufferedReader reader;

	/** Konstruktor. <br />
	 *  <br />
	 *  Erzeugt einen neuen Socket mit der angegebenen Portnummer zu dem
	 *  angegebenen Host.
	 *  <br />
	 *  @param hostname der Hostname des Servers
	 *  @param port der Port, auf dem der Server horcht
     *  @throws IOException
	 */
	public Socket(String hostname, int port) throws IOException {
		this.hostname = hostname;
		this.port = port;
	}
	/** Konstruktor. <br />
	 *  <br />
	 *  Erzeugt einen neuen Socket mit der angegebenen Socket.
	 *  Wird vom ServerSocket benutzt.
	 *  <br />
	 *  @param socket der Socket, mit dem das Socket-Objekt arbeiten soll
     *  @throws IOException
	 */
	public Socket(java.net.Socket socket) throws IOException {
		this.socket = socket;
		this.port = socket.getPort();
		this.hostname = socket.getRemoteSocketAddress().toString();
		reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
	}
	/** Schließt den Socket.  <br />
	 *  Nach dem Schließen des Socket ist das Socket-Objekt
	 *  nicht mehr verbunden.<br />
     *  @throws IOException
	 */
	public void close() throws IOException {
		reader.close();
		socket.close();
	}
	/** Verbindet das Socket-Objekt zu der entsprechenden
	 *  Portnummer auf dem entsprechenden Host.  <br />
  	 *  <br />
	 *  Ging alles gut, ist das Socket-Objekt verbunden.
  	 *  <br />
  	 *  @return  true, wenn die Verbindung akzeptiert wurde
	 */
	public boolean connect() {
		try {
		  socket = new java.net.Socket(hostname, port);
		  reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
		}
		catch (Exception e) {
			return false;
		}
		return true;
	}

//	--- Implementierung Leseoperationen ------------------------------

	/** Prüfen, ob Daten verfügbar sind. <br />
  	 *  <br />
  	 *  Liefert die Anzahl der Bytes, die vom Socket gelesen werden können,
	 *  ohne beim nächsten Aufruf von read() zu blockieren.<br />
  	 *  <br />
  	 *  @return Anzahl der verfügbaren Bytes
     *  @throws IOException
  	 */
	public int dataAvailable() throws IOException {
		return socket.getInputStream().available();
	}

  	/** Lesen eines Zeichens vom Socket. <br />
  	 *  <br />
  	 *  Das Zeichen wird als int im Bereich zwischen 0 bis 255 geliefert.
  	 *  Die Methode blockiert, bis Eingabe-Daten verfügbar sind.
  	 *  Im Fehlerfall liefert sie -1 oder erzeugt eine Ausnahme. <br />
  	 *  <br />
  	 *  @return Das gelesene Byte (0..255) oder -1
     *  @throws IOException
  	 */
	public int read() throws IOException {
		return socket.getInputStream().read();
	}
	/** Lesen von Bytes vom Socket. <br />
	 *  <br />
  	 *  Die Methode blockiert, bis Eingabe-Daten verfügbar sind.
  	 *  Im Fehlerfall liefert sie -1 oder erzeugt eine Ausnahme. <br />
	 *  <br />
	 *  @return Die Anzahl der gelesenen Bytes oder -1
	 *  @param b Der Buffer
	 *  @param len Maximale Anzahl der zu lesenden Bytes
	 */
	public int read(byte[] b, int len) throws IOException {
		return socket.getInputStream().read(b, 0, len);
	}
	/** Lesen eines Strings vom Socket. <br />
	 *  <br />
	 *  Die Methode liest eine Zeile, die durch ein Zeilenendezeichen
	 *  ('\n' linefeed) abgeschlossen sein muss. Der zurückgegebene
	 *  String enthält die Zeile ohne Zeilenendezeichen.
  	 *  Die Methode blockiert, bis Eingabe-Daten verfügbar sind.
  	 *  Im Fehlerfall liefert sie -1 oder erzeugt eine Ausnahme. <br />
	 *  <br />
	 *  <br />
	 *  @return Der String oder null
     *  @throws IOException
	 */
	public String readLine() throws IOException {
		return reader.readLine();
	}

	//	--- Implementierung Schreiboperationen ---------------------------

	/** Schreiben eines Zeichens auf den Socket. <br />
	 *  <br />
	 *  Diese Methode schreibt ein Byte (die niederwertigen 8 Bit des
	 *  Parameters b) auf den Socket.
	 *  <br />
	 *  @param b Das zu schreibende Byte (0..255)
	 */
	public void	write(int b) throws IOException {
		socket.getOutputStream().write(b);
	}
  	/** Schreiben von Bytes auf den Socket. <br />
	 *  <br />
	 *  Diese Methode schreibt mehrere Bytes auf den Socket.
	 *  <br />
	 *  @param b Der Buffer
	 *  @param len Maximale Anzahl der zu schreibenden Bytes
     *  @throws IOException
  	 */
	public void write(byte[] b, int len) throws IOException {
		socket.getOutputStream().write(b, 0, len);
	}
 	/** Schreiben eines String auf den Socket. <br />
	 *  <br />
	 *  Diese Methode schreibt einen String zeichenweise auf den Socket.
	 *  <br />
	 *  @param s Der String
     *  @throws IOException
  	 */
	public void write(String s) throws IOException {
		socket.getOutputStream().write(s.getBytes());
	}
}


Kann mir einer sagen wo mein Fehler liegt? Laut eurem Links dürfte es doch nicht soo falsch sein oder?

Danke schonmal
MfG Noxum
 
Hallo,

hier mal ein paar Hints:

Bezüglich deinem "Sender" ... in Zeile 13: fis.read(data);

... was gibt den fis.read(...) zurück?
Wurde die Datei überhaupt komplett gelesen? -> int bytesRead = fis.read(...);
Du unterstellst: bytesRead == data.length

Analog in deinem Empfänger in Zeilen 16-19 ... InputStream.read(...) liest nur die Daten die verfügbar sind (available)... das sind nicht notwendigerweise die gesamten Daten...

deshalb gibt es in dem letzten Link oben auch das Idiom:
Java:
...   
                byte[] buffer = new byte[16384];
                int len = 0;
                while ((len = inputStream.read(buffer)) > 0) {
                    outputStream.write(buffer, 0, len);
                }
...

Ansonsten klingt 8192bytes genau nach der default Buffergröße des BufferedReader ...
http://www.sws.bfh.ch/~amrhein/Swing/javainsel7/javainsel_13_007.htm#t2t34

Gruß Tom
 
Okey,
fis.read() gibt immerhin schonmal den gleichen Wert wie data.length aus, dass dies nicht zwangsläufig sein muss stimmt wohl.

Dann hab ich versucht, genau die Anzahl von Bytes auszulesen welche vorhanden sein müssten, sprich der wert , welcher von fis.read() zurück gegeben wird.
Da kommt allerdings wieder nur 8192Byte bei raus.

Also habe ich versucht die Größe des BufferedReader manuel auf 100 000 Byte zu setzen und hab dies dann in der oben geposteten Socketklasse so verändert:
Java:
 reader = new BufferedReader(new InputStreamReader(socket.getInputStream()),100000);

Allerdings kommt hier immernoch nur 8192 Bytes an.


Sagen wir mal mein Bild welches ich senden möchte ist 26154Byte groß, der BufferedReader wurde mit einer Größe von 100 000 Byte erstellt. Wenn ich jetzt diese 26154 Byte auf meinen Stream schreibe und auf der anderen Seite genau 26154 Byte auslese (sagen wir mal die andere Seite kennt die Größe schon), dann müsste dies doch auch ohne den von dir gepostet Codeteil gehen oder verstehe ich da was falsch?


Danke für deine Mühe :)
 
Huhu,

Du musst wie Tom sagt nicht nur so lange senden bis die gesamte Datei durchgewandert ist, sondern auf der anderen Seite auch so lange lesen bis alle Daten "durch" sind.

8192 bytes hören sich wie bereits gesagt verdächtig nach der Standardpuffergröße an.

Am Ende der Schreibschleife sollte auch ein flush() folgen, damit gepufferte Daten gesendet werden.
Auf der anderen Seite musst du eben einfach nur auslesen bis alle Daten angekommen sind (wann auch immer das sein mag).

Der Puffer ist nur dazu da um zu "puffern", also zum Beispiel auf neue Daten zu warten anstatt jedes Mal zu senden. Wenn du die Puffergröße erhöhst, dann wartet der Puffer eben länger bis er sendet (also voll ist).
Am Ende sollte man flush() aufrufen um sicher zu gehen, dass noch zwischengespeicherte Daten gesendet werden.

Die ganze Datei in den Speicher einzulesen ist eine schlechte Idee.
Was ist, wenn die Datei 1Gb groß ist? Dann schreibst du dir vielleicht deinen ganzen RAM voll, wenn du nicht schon eine OutOfMemoryException bekommst.
Am einfachsten ist es zum Beispiel X Bytes aus der Datei auszulesen und diese gleich zu senden.
Dann füllt man selben Puffer wieder mit X Bytes aus der Datei und sendet - somit belegt man nur X Bytes Speicher.

Also:

Code:
Definition von puffer
Solange Datei nicht durch:
    puffer = Weitere X-Bytes aus Datei
    sende puffer
    flush

Datei schließen, Verbindung schließen,...

Auf der anderen Seite dann das Umgekehrte:
Man lese bis der Puffer voll ist und schreibe den Inhalt des Puffers auf die Platte.
Danach wird der Puffer wieder befüllt usw.

Ich habe gemerkt, dass du für die Menge der zu lesenden Bytes InputStream.available() heranziehst was du nicht machen solltest da ja nie alle Daten gleichzeitig zur Verfügung stehen sondern zum Beispiel nur 8192 gepufferte Bytes.

Das Ganze bedarf also einer Überarbeitung.


Gruß,
 
Zuletzt bearbeitet:
Vielen Dank für deine Antwort, sie hat mir sehr geholfen!

Leider haben wir in der Schule noch keine Daten in Form von ganzen Datein mittels Sockets versendet, bisher wurden Daten nur als String oder Integer übertragen.
Als ich mir überlegt habe wie ich eine Datei senden kann, ist mir auch schon die Idee gekommen, die Datei in kleine Stückchen zu zerlegen und diese dann einzeln zu senden (Hätte ich mal an der Idee festgehalten^^).
Naja aber aus Fehlern lernt man ja, dass mit dem flush() wusste ich aber auch noch nicht.
Ich werde mir deine Tipps zu herzen nehmen und meinen Code nocheinmal überarbeiten.

Danke,
Noxum
 

Neue Beiträge

Zurück