Socket Einfacher filetransfer

stulleman

Bekanntes Mitglied
Hallo Zusammen!
Ich versuche eine Datei übers Netzwerk zu schicken.
Ich habe eine FileSender Klasse:
Java:
package de.pk.maximilian.util;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;

public class FileSender {

	private boolean connected;
	private Socket socket;
	private DataOutputStream out;
	
	public FileSender() {
		connectToHost("localhost", 6493);
		sendFile(new File("C:/Users/Max/Desktop/test.txt"));
	}
	
	public void connectToHost(String ip, int port) {
		try {
			socket = new Socket(ip, port);
		} catch (UnknownHostException e) {
			e.printStackTrace();
			System.exit(0);
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(0);
		}
		
		try {
			out = new DataOutputStream(socket.getOutputStream());
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(0);
		}
		
		connected = true;
	}
	
	public void sendFile(File file) {
		FileInputStream fin = null;
		byte[] dataBuffer = new byte[(int) file.length()];
		
		try {
			fin = new FileInputStream(file);
			fin.read(dataBuffer);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		byte[] nameBuffer = file.getName().getBytes();
		
		try {
			out.write(nameBuffer.length);
			out.write(nameBuffer);
			
			out.write(dataBuffer.length);
			out.write(dataBuffer);
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public boolean isConnected() {
		return connected;
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		new FileSender();
	}
}

Und eine FileReciever Klasse:

Java:
package de.pk.maximilian.util;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class FileReciever {

	private ServerSocket serverSocket;
	private DataInputStream in = null;
	
	public FileReciever() {
		waitForSender(6493);
		waitForFile();
	}
	
	public void waitForSender(int port) {
		Socket clientSocket;
		
		try {
			serverSocket = new ServerSocket(port);
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		try {
			clientSocket = serverSocket.accept();
			in = new DataInputStream(clientSocket.getInputStream());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public void waitForFile() {
		File file;
		FileOutputStream fout;
		byte[] nameBuffer = null;
		byte[] dataBuffer = null;
		
		try {
			nameBuffer = new byte[in.read()];
			in.read(nameBuffer);
			
			dataBuffer = new byte[in.read()];
			in.read(dataBuffer);
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		String name = new String(nameBuffer);
		
		file = new File("C:/Users/Max/Desktop/" + "copy_" + name);
		
		try {
			fout = new FileOutputStream(file);
			fout.write(dataBuffer);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		new FileReciever();
	}
}

Und mit einer einfachen Textdatei klappt das auch wunderbar, aber wenn ich dann ein großes Bild schicken will sendet er nur die Hälfte. Dann habe ich mal rum gespielt, und gemerkt das er nur Textdateien bis 92 bytes versendet. Habt ihr eine Idee woran das liegen kann?

Danke schonmal!
 

Dit_

Bekanntes Mitglied
ich würde das Empfangen in einer Schleife implementieren. Ich meine die LAN-Pakete haben ja eine endliche größe, also müssen die Daten packetweise übertragen werden. Bei kleineren Dateien funktioniert das auch ohne schleife da alle Daten in ein Paket passen.
 

stulleman

Bekanntes Mitglied
Ich kann mir gerade nicht vorstellen wie das aussehen sollte, das empfangen der Pakete in einer Schleife zu implementieren... Vielleicht könntest du mir einen Pseudo-Code schicken oder so (;
Danke!
 

stulleman

Bekanntes Mitglied
Das Problem mit dem abbrechen nach einer bestimmten Anzahl an bytes habe ich alleine in den Griff bekommen. Mein Problem jetzt ist das ich für ein ca. 3.4 Mb großes Bild fast 8 Minuten brauche um es zu senden. Das Problem liegt denke ich nicht an der Übertragung, sondern beim konvertieren des Bildes in ein byte Array. Deswegen habe ich es so versucht:

Java:
package de.pk.maximilian.util;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;

import javax.imageio.ImageIO;

public class FileSender {

	private Socket socket;
	private DataOutputStream out;
	
	public FileSender() {
		connectToHost("localhost", 6493);
		sendFile(new File("C:/Users/Max/Desktop/test.jpg"));
	}
	
	public void connectToHost(String ip, int port) {
		try {
			socket = new Socket(ip, port);
		} catch (UnknownHostException e) {
			e.printStackTrace();
			System.exit(0);
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(0);
		}
		
		try {
			out = new DataOutputStream(socket.getOutputStream());
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(0);
		}
	}
	
	public void sendFile(File file) {
		String[] tokens = file.getName().split("\\.");
		String file_extension = tokens[tokens.length-1];
		
		byte[] nameBuffer = file.getName().getBytes();
		byte[] dataBuffer = null;
		
		if(file_extension.equals("jpg")) {
			BufferedImage image = null;
			
			try {
				image = ImageIO.read(file);
			} catch (IOException e) {
				e.printStackTrace();
			}
			
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			
		    try {
				ImageIO.write(image, "jpg", baos);
			} catch (IOException e) {
				e.printStackTrace();
			}
			
		    dataBuffer = baos.toByteArray();
		    
		} else {
			
			dataBuffer = new byte[(int) file.length()];
			FileInputStream fin = null;
			
			try {
				fin = new FileInputStream(file);
				fin.read(dataBuffer);
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		try {
			out.writeInt(nameBuffer.length);
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		for(int i = 0; i < nameBuffer.length; i++) {
			try {
				out.write(nameBuffer[i]);
			} catch(IOException e) {
				e.printStackTrace();
			}
		}
		
		try {
			out.writeInt(dataBuffer.length);
		} catch(IOException e) {
			e.printStackTrace();
		}
		
		for(int i = 0; i < dataBuffer.length; i++) {
			
			double h = (i * 100) / dataBuffer.length;
			System.out.println(h + "%");
			
			try {
				out.write(dataBuffer[i]);
			} catch(IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		new FileSender();
	}
}

Aber das bringt auch keine Zeitverbesserung...
Habt ihr eine Idee?
 
S

SlaterB

Gast
ein Bild kannst du wie jede andere Datei auch einlesen, wozu der Unterschied?
teste Dateien verschiedener Größe, statt 3.4 MB gehe auf 1 MB runter und immer kleiner, sinkt die Zeit proportional?
logge im Programm, wo wieviel Zeit verbraucht wird, beim Einlesen oder allein der einzelnen Senden-Zeile?
bzw. Empfangen auf der Gegenseite?

teste in einer Schleife 1000x bzw. anfangs weniger ein immer gleiches Test-byte-Array von Länge 5000 zu verschicken,
dauern diese insgesamt 5 MB auch 10 Min. bzw. 600 sec? werden pro Sekunde ca. 10 Schleifendurchläufe geschafft?

edit:
bzw. nun sehe ich dass es ja gar keine einzelne Zeile ist, sondern du jedes byte einzeln schickst,
unterlasse dies!
schicke das ganze Array oder zumindest große Blöcke von z.B. 8192 Bytes
 
Zuletzt bearbeitet von einem Moderator:

stulleman

Bekanntes Mitglied
Gut danke erst mal für den Hinweis das es keinen Unterschied macht ob Bild oder normale Textdatei (;
Erspart mir viel Arbeit!
Nun zu den Blöcken, soll ich einfach in einer while-Schleife Teile vom ganzen Array schicken?
z.B. so:

Java:
int cur = 0;

while(cur < file.length) {
    if(cur + 8192 <= file.length) {
       out.write(dataBuffer, cur, 8192);
       cur += 8192;
    } else {
       out.write(dataBuffer, cur, file.length - cur);
       cur += file.length - cur;
    }
}

habe das jetzt nur schnell hin geschmiert also wäre ich dankbar für Verbesserungen (;
 
S

SlaterB

Gast
sieht gar nicht schlecht aus, probier doch mal ob es schon schneller geht,
und was ist mit einfach nur
out.write(dataBuffer);
?
dann kannst du sicherlich nicht mehr gut Prozent ausrechnen,
dafür dauern kleine Dateien eben auch nicht mehr 8 Min, ein Test wäre es wert,

dürfte gegenüber der 8192er-Variante nur unwesentlich länger sein, diese kannst du also verwenden falls das besser passt,
Konstanten wie 8192 übrigens nicht mehrfach schreiben sondern in eine Variable stecken und die Variable verwenden,
besser für evtl. Änderungen
 

stulleman

Bekanntes Mitglied
Wauh super, innerhalb von 1 Sekunde hat er das Bild übertragen!
Mit out.write(dataBuffer) hatte ich am Anfang Probleme da ich immer einen Socket write error bekommen habe...
Aber jetzt klappt ja alles wunderbar! Danke!
 

Kr0e

Gesperrter Benutzer
Bitte vermeide es, in einer Schleife ständig auf file.length() zuzugreifen. Speichere die länge initial in einer long Variable.

Java:
byte[] buffer = new byte[0xFFFF]; // 65536
int read;

while((read = imageInputStream.read(buffer)) != -1) {
    socketOutputStream.write(buffer, 0, read);
}

// EOF reached...
imageInputStream.close();

Warum machst du es so kompliziert ? Einfach nen inputStream zum Image öffnen und partiell senden. Ich verstehe nicht, wieso du dein Image in erst komplett in den Speicher einliest ? Das macht doch dann garkeinen Sinn, wenn man es partiell senden will. Der Sinn ist, dass der Speicher nicht zugemüllt wird, sondern immer nur kleine Chunks übertagen werden. Da das Bild ja offensichtlich auf deiner Festplatte ist, öffne einfach einen FileInputStream zu dieser Datei....

PS:

Nochwas!

Dein Code hier:

Java:
            dataBuffer = new byte[(int) file.length()];
            FileInputStream fin = null;
            
            try {
                fin = new FileInputStream(file);
                fin.read(dataBuffer);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

Ist ein Problem: 1. schließt du deinen File stream nicht, das ist unschön. 2. Viel gefährlicher ist, dass du davon ausgehst, dass die Datei auf jeden Fall geladen wird. Wenn man sich die Doku zum InputStrema anschaut, so steht da, das read einen Intwert zurück gibt, welcher die Anzahl an Bytes beschreit die erfolgreich gelesen wurden., Es kann passieren, dass dein OS dir vlt nur die Hälfte des Arrays gestattet auf einmal zu lesen, womit deine Datei natürlich nicht vollständig ist, selbst wenn das sendne klapppt ;)
 
Zuletzt bearbeitet:
Ähnliche Java Themen

Ähnliche Java Themen

Neue Themen


Oben