This question already has answers here:
Java multiple file transfer over socket
(3 answers)
Closed 6 years ago.
I am first transferring a file from a client to my master, the stores the byte array and then sends to the slave. Where the slave stores the byte array. But when The file is sent properly from client to master but when I send the byte array to the slave it to the slave the read method in input stream constantly reads 0.
// This method writes the file to the master
public void writeFile(File file) {
try {
this.write(String.valueOf(file.length()));
byte[] bytearray = new byte[(int) file.length()];
FileInputStream fin = new FileInputStream(file);
BufferedInputStream bin = new BufferedInputStream(fin);
bin.read(bytearray, 0, bytearray.length);
BufferedOutputStream bos;
OutputStream os = socket.getOutputStream();
bos= new BufferedOutputStream(os);
bos.write(bytearray, 0, bytearray.length);
bos.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
//This method reads the file into the master as a byte array and the byte array from the master into slave
public byte[] readFile() {
byte[] bytearray = null;
try {
int currentTot = 0;
int filesize = Integer.parseInt(this.read());
System.out.println(filesize);
bytearray = new byte[filesize];
InputStream is = socket.getInputStream();
int bytesRead;
bytesRead = is.read(bytearray, 0, bytearray.length);
currentTot = bytesRead;
int count = 0;
do {
bytesRead = is.read(bytearray, currentTot, (bytearray.length - currentTot));
if (bytesRead > 0) {
currentTot += bytesRead;
count = 0;
} else {
count++;
System.out.println("count " + count);
}
} while (bytesRead > -1);
System.out.println(currentTot);
// bos.write(bytearray, 0, currentTot);
// bos.flush();
// bos.close();
} catch (IOException e) {
e.printStackTrace();
}
return bytearray;
}
//This method writes from the master to the slave
public void writeByte(byte[] m) {
this.write(String.valueOf(m.length));
System.out.println("File side inside sender" + m.length);
// byte[] bytearray = m;
OutputStream os;
try {
os = socket.getOutputStream();
os.write(m, 0, m.length);
os.flush();
//os.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Interestingly if I close my output stream after I send my byte array from my master it works well. But I cannot close stream because the slave needs to communicate with the master further. Thanks in advance.
public void write(String output) {
if (pw == null)
this.openWriter();
pw.println(output);
}
public String read() {
try {
if (br == null) {
if (this.socket != null)
br = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
}
return br.readLine();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
You're misreading the file length in the receiver. You are getting zero, so you're constructing a zero length byte array, so read() returns zero.
You need to send the length via DataOutputStream.writeLong() and read it via DataInputStream.readLong(). And then your sending and receiving code is all wrong as well. See my answer here for complete code.
Related
I am learning sockets and now I want to write file transfer program. I have server part and client part. Server part contains 2 ports: 5000 (commands) and 5001 (files). Now I want to send a file via socket and when I did something is wrong because only 425B of data is sending.
Here is client send method:
private void sendFile(Socket socket) {
File file2 = new File("C:\\Users\\barte\\Desktop\\dos.png");
byte[] bytes = new byte[16 * 1024];
System.out.println(file2.exists());
try (InputStream inputStream = new FileInputStream(file2);
OutputStream outputStream = socket.getOutputStream();
OutputStream secondOutput = new FileOutputStream("C:\\Users\\barte\\Desktop\\received\\dos.png")) {
int count;
while ((count = inputStream.read(bytes)) > 0) {
outputStream.write(bytes, 0, count);
secondOutput.write(bytes, 0, count);
}
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
As you can see (image below) I am writing this file also locally and everything is ok, all of 73KB of data is writed.
Now, on server side I am trying to receive this file:
case SEND: {
new Thread(() -> {
printWriter.println("Server is receiving files right now...");
try (ServerSocket serverSocket = new ServerSocket(5001)) {
while (true) {
new FilesTransfer(serverSocket.accept()).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
break;
}
And inside FilesTransfer run method:
#Override
public void run() {
System.out.println("Hello there");
try {
InputStream inputStream = inSocket.getInputStream();
OutputStream outputStream = new FileOutputStream("C:\\Users\\barte\\Desktop\\received\\file");
byte[] bytes = new byte[16 * 1024];
int count;
while ((count = inputStream.read()) > 0) {
outputStream.write(bytes, 0, count);
}
outputStream.close();
inputStream.close();
inSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Where is a bug? Why only empty bytes are sending when locally everything it's fine?
The problem is:
while ((count = inputStream.read()) > 0) {
Your code uses InputStream.read(), which reads individual bytes (or -1 when end-of-stream). Right now, you are reading individual bytes, interpreting that as a length, and then writing that number of 0x00 bytes from bytes to the file. This stops when you read a 0x00 byte from the stream.
You need to change this to use InputStream.read(byte[]):
while ((count = inputStream.read(bytes)) != -1) {
That is, you need to pass bytes in, and check for the result being unequal to -1, not if it is greater than zero (0), although read(byte[]) will only return 0 if the passed in byte array has length zero, so that is not a real concern.
You could do it in this way:
#Override
public void run() {
System.out.println("Hello there");
try {
InputStream inputStream = inSocket.getInputStream();
OutputStream outputStream = new FileOutputStream("C:\\Users\\barte\\Desktop\\received\\file");
byte[] bytes = new byte[16 * 1024];
int byteRead= 1;
while (byteRead > -1) {
byteRead= inputStream.read();
outputStream.write(byteRead);
}
outputStream.close();
inputStream.close();
inSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Actually END OF FILE or EOF means -1 and you did > 0 so 0 was taken and it stopped the connection saving the file.
I also recommend to write a logic to transfer the filename as a command to the server so that the file is saved with the correct name and extension!
I am just trying to send some files from a socket and i am able to send those files without any interruption: also whether the size file is small or large that does not matter it sends like a charm.
But the problem in my case that is arising is the file that i sent is being corrupted, i.e. it is not playing like audio or video. I have already gone through this but it did not helped.
The code that I am using is below.
Server Side:
File file = new File(
Environment.getExternalStorageDirectory(),
"testingFile.mp4");
byte[] mybytearray = new byte[4096];
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
} catch (FileNotFoundException ex) {
ex.printStackTrace();
}
BufferedInputStream bis = new BufferedInputStream(fis);
DataInputStream dis = new DataInputStream(bis);
OutputStream os;
DataOutputStream dos = null;
try {
os = socket.getOutputStream();
dos = new DataOutputStream(os);
dos.writeUTF(file.getName());
dos.writeLong(mybytearray.length);
int read;
while ((read = dis.read(mybytearray)) != -1) {
dos.write(mybytearray, 0, read);
}
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
if (dos != null) {
dos.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
And the Client Side :
File file = new File(
Environment.getExternalStorageDirectory(),
"TEST SUCCESS.mp4");
InputStream in = null;
int bufferSize;
try {
bufferSize = socket.getReceiveBufferSize();
in = socket.getInputStream();
DataInputStream clientData = new DataInputStream(in);
String fileName = clientData.readUTF();
System.out.println(fileName);
OutputStream output = new FileOutputStream(
file);
byte[] buffer = new byte[bufferSize];
int read;
while ((read = clientData.read(buffer)) != -1) {
output.write(buffer, 0, read);
}
output.flush();
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (in != null) {
in.close();
}
}
Thanks in advance.
So after the conversations in comments and as #MarquisofLorne told to delete the line that i have written in my server side code. i.e either delete this line from server side code:
dos.writeLong(mybytearray.length);
or write this below line code in client side code:
long sizeOfFile = clientData.readLong();
It solves the problem.
Server Side
Your code sends buffer length(4096), which is a bug.
It should send file length.
File file = new File( ... );
try {
//dos.writeLong(mybytearray.length);
dos.writeLong(file.length());
}
Client Side
Server sends two meta data
file name( F bytes, encoded by utf-8)
file length (8 bytes)
And then sends entire contents( N bytes)
But client code ignores file length(8bytes), just reads file name and contents N bytes
in = socket.getInputStream();
DataInputStream clientData = new DataInputStream(in);
String fileName = clientData.readUTF(); // ok read F bytes
// missing readLong(..) 8 bytes
// long fileLen = clientData.readLong(); <= read file length before reading contents
// read N bytes, but first 8 bytes are file length, which are written into file.
int read;
while ((read = clientData.read(buffer)) != -1) {
output.write(buffer, 0, read);
}
Don't rely on -1
Your codes keep relying on -1 in while loop
while ((read = dis.read(mybytearray)) != -1) {
dos.write(mybytearray, 0, read);
}
while ((read = clientData.read(buffer)) != -1) {
output.write(buffer, 0, read);
}
-1 means abnormal state.
Because server knows the exact size of a file and writes out the file, client should read the exact length of bytes from stream.
If server send 1234 bytes, when client read -1 from clientData.read(..), it fails to read contents from stream, not end of stream.
Question at the bottom
I'm using netty to transfer a file to another server.
I limit my file-chunks to 1024*64 bytes (64KB) because of the WebSocket protocol. The following method is a local example what will happen to the file:
public static void rechunck(File file1, File file2) {
FileInputStream is = null;
FileOutputStream os = null;
try {
byte[] buf = new byte[1024*64];
is = new FileInputStream(file1);
os = new FileOutputStream(file2);
while(is.read(buf) > 0) {
os.write(buf);
}
} catch (IOException e) {
Controller.handleException(Thread.currentThread(), e);
} finally {
try {
if(is != null && os != null) {
is.close();
os.close();
}
} catch (IOException e) {
Controller.handleException(Thread.currentThread(), e);
}
}
}
The file is loaded by the InputStream into a ByteBuffer and directly written to the OutputStream.
The content of the file cannot change while this process.
To get the md5-hashes of the file I've wrote the following method:
public static String checksum(File file) {
InputStream is = null;
try {
is = new FileInputStream(file);
MessageDigest digest = MessageDigest.getInstance("MD5");
byte[] buffer = new byte[8192];
int read = 0;
while((read = is.read(buffer)) > 0) {
digest.update(buffer, 0, read);
}
return new BigInteger(1, digest.digest()).toString(16);
} catch(IOException | NoSuchAlgorithmException e) {
Controller.handleException(Thread.currentThread(), e);
} finally {
try {
is.close();
} catch(IOException e) {
Controller.handleException(Thread.currentThread(), e);
}
}
return null;
}
So: just in theory it should return the same hash, shouldn't it? The problem is that it returns two different hashes that do not differ with every run.. file size stays the same and the content either.
When I run the method once for in: file-1, out: file-2 and again with in: file-2 and out: file-3 the hashes of file-2 and file-3 are the same! This means the method will properly change the file every time the same way.
1. 58a4a9fbe349a9e0af172f9cf3e6050a
2. 7b3f343fa1b8c4e1160add4c48322373
3. 7b3f343fa1b8c4e1160add4c48322373
Here is a little test that compares all buffers if they are equivalent. Test is positive. So there aren't any differences.
File file1 = new File("controller/templates/Example.zip");
File file2 = new File("controller/templates2/Example.zip");
try {
byte[] buf1 = new byte[1024*64];
byte[] buf2 = new byte[1024*64];
FileInputStream is1 = new FileInputStream(file1);
FileInputStream is2 = new FileInputStream(file2);
boolean run = true;
while(run) {
int read1 = is1.read(buf1), read2 = is2.read(buf2);
String result1 = Arrays.toString(buf1), result2 = Arrays.toString(buf2);
boolean test = result1.equals(result2);
System.out.println("1: " + result1);
System.out.println("2: " + result2);
System.out.println("--- TEST RESULT: " + test + " ----------------------------------------------------");
if(!(read1 > 0 && read2 > 0) || !test) run = false;
}
} catch (IOException e) {
e.printStackTrace();
}
Question: Can you help me chunking the file without changing the hash?
while(is.read(buf) > 0) {
os.write(buf);
}
The read() method with the array argument will return the number of files read from the stream. When the file doesn't end exactly as a multiple of the byte array length, this return value will be smaller than the byte array length because you reached the file end.
However your os.write(buf); call will write the whole byte array to the stream, including the remaining bytes after the file end. This means the written file gets bigger in the end, therefore the hash changed.
Interestingly you didn't make the mistake when you updated the message digest:
while((read = is.read(buffer)) > 0) {
digest.update(buffer, 0, read);
}
You just have to do the same when you "rechunk" your files.
Your rechunk method has a bug in it. Since you have a fixed buffer in there, your file is split into ByteArray-parts. but the last part of the file can be smaller than the buffer, which is why you write too many bytes in the new file. and that's why you do not have the same checksum anymore. the error can be fixed like this:
public static void rechunck(File file1, File file2) {
FileInputStream is = null;
FileOutputStream os = null;
try {
byte[] buf = new byte[1024*64];
is = new FileInputStream(file1);
os = new FileOutputStream(file2);
int length;
while((length = is.read(buf)) > 0) {
os.write(buf, 0, length);
}
} catch (IOException e) {
Controller.handleException(Thread.currentThread(), e);
} finally {
try {
if(is != null)
is.close();
if(os != null)
os.close();
} catch (IOException e) {
Controller.handleException(Thread.currentThread(), e);
}
}
}
Due to the length variable, the write method knows that until byte x of the byte array, only the file is off, then there are still old bytes in it that no longer belong to the file.
This question already has answers here:
Java multiple file transfer over socket
(3 answers)
Closed 5 years ago.
I have written a small client-server code for transferring small file. It uses Data output stream and readFully() method of data input stream. This code does not work for larger files for obvious reasons. I was thinking of fragmenting large files into smaller chunks of 1Kb each before sending them to client. But I can't think of any solution (like how to write multiple chunks on data output stream with correct offset and how to reassemble them at receiving end. Can anyone provide a workaround? It would be very helpful if you could modify my code:
Sender (Server):
public void sendFileDOS() throws FileNotFoundException {
runOnUiThread( new Runnable() {
#Override
public void run() {
registerLog("Sending. . . Please wait. . .");
}
});
final long startTime = System.currentTimeMillis();
final File myFile= new File(filePath); //sdcard/DCIM.JPG
byte[] mybytearray = new byte[(int) myFile.length()];
FileInputStream fis = new FileInputStream(myFile);
BufferedInputStream bis = new BufferedInputStream(fis);
DataInputStream dis = new DataInputStream(bis);
try {
dis.readFully(mybytearray, 0, mybytearray.length);
OutputStream os = socket.getOutputStream();
//Sending file name and file size to the client
DataOutputStream dos = new DataOutputStream(os);
dos.writeUTF(myFile.getName());
dos.writeLong(mybytearray.length);
int i = 0;
final ProgressBar myProgBar=(ProgressBar)findViewById(R.id.progress_bar);
while (i<100) {
dos.write(mybytearray, i*(mybytearray.length/100), mybytearray.length/100);
final int c=i;
runOnUiThread( new Runnable() {
#Override
public void run() {
myProgBar.setVisibility(View.VISIBLE);
registerLog("Completed: "+c+"%");
myProgBar.setProgress(c);
if (c==99)
myProgBar.setVisibility(View.INVISIBLE);
}
});
i++;
}
dos.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
runOnUiThread( new Runnable() {
#Override
public void run() {
long estimatedTime = (System.currentTimeMillis() - startTime)/1000;
registerLog("File successfully sent");
registerLog("File size: "+myFile.length()/1000+" KBytes");
registerLog("Elapsed time: "+estimatedTime+" sec. (approx)");
registerLog("Server stopped. Please restart for another session.");
final Button startServerButton=(Button)findViewById(R.id.button1);
startServerButton.setText("Restart file server");
}
});
}
Receiver (Client):
public class myFileClient {
final static String servAdd="10.141.21.145";
static String filename=null;
static Socket socket = null;
static Boolean flag=true;
/**
* #param args
*/
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
initializeClient();
receiveDOS();
}
public static void initializeClient () throws IOException {
InetAddress serverIP=InetAddress.getByName(servAdd);
socket=new Socket(serverIP, 4444);
}
public static void receiveDOS() {
int bytesRead;
InputStream in;
int bufferSize=0;
try {
bufferSize=socket.getReceiveBufferSize();
in=socket.getInputStream();
DataInputStream clientData = new DataInputStream(in);
String fileName = clientData.readUTF();
System.out.println(fileName);
OutputStream output = new FileOutputStream("//home//evinish//Documents//Android//Received files//"+ fileName);
long size = clientData.readLong();
byte[] buffer = new byte[bufferSize];
while (size > 0
&& (bytesRead = clientData.read(buffer, 0,
(int) Math.min(buffer.length, size))) != -1) {
output.write(buffer, 0, bytesRead);
size -= bytesRead;
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Please help! Thanks in advance! :)
You're right, this is a poor way to do it. It wastes both memory and time; it assumes the file size is 32 bits; it assumes the entire file fits into memory; it assumes the entire file is read in one read; and it doesn't send anything until the entire file has been read.
The canonical way to copy a stream in Java is this:
while ((count = in.read(buffer)) > 0)
{
out.write(buffer, 0, count);
}
It will work with any size buffer you like and therefore with any size file you can come up with. Use the same code at both ends, although you don't have to use the same size buffer at both ends. As you're copying over a network you might think that 1k or 1.5k is the best size, but that overlooks the presence of the socket send and receive buffers in the kernel. When you take them into account it is probably better to use 8k or more.
I finally solved the problem. Here is my modified source code for server and client. Hope this would help other people too! :)
Server Side code snippet (sender):
final File myFile= new File(filePath); //sdcard/DCIM.JPG
byte[] mybytearray = new byte[8192];
FileInputStream fis = new FileInputStream(myFile);
BufferedInputStream bis = new BufferedInputStream(fis);
DataInputStream dis = new DataInputStream(bis);
OutputStream os;
try {
os = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
dos.writeUTF(myFile.getName());
dos.writeLong(mybytearray.length);
int read;
while((read = dis.read(mybytearray)) != -1){
dos.write(mybytearray, 0, read);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Client side code snippet (Receiver):
int bytesRead;
InputStream in;
int bufferSize=0;
try {
bufferSize=socket.getReceiveBufferSize();
in=socket.getInputStream();
DataInputStream clientData = new DataInputStream(in);
String fileName = clientData.readUTF();
System.out.println(fileName);
OutputStream output = new FileOutputStream("//home//evinish//Documents//Android//Received files//"+ fileName);
byte[] buffer = new byte[bufferSize];
int read;
while((read = clientData.read(buffer)) != -1){
output.write(buffer, 0, read);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
A bit faster way of writing to output stream:
long acc=0;
long N=myFile.length();
while(acc<N){
noofbytes=dis.read(mybytearray, 0, 16384);
dos.write(mybytearray, 0, noofbytes);
acc=acc+noofbytes; } dos.flush();
I saved around 7 seconds while transferring a video file of 72MB.
I am trying to encrypt images on android, send them to the server so that it process them. The server has to decrypt the received message. I already posted the code in this question
I have called the encrypt function on the Android side and the decrypt function the java server side (image is sent via TCP).
However, I am receiving the error:
javax.crypto.BadPaddingException: Given final block not properly padded
I output the key on android and got:
javax.crypto.spec.SecretKeySpec#652
whereas on the Java server (developped using Netbeans) I got:
javax.crypto.spec.SecretKeySpec#148dd
I thought that the padding is different so I used
AES/ECB/PKCS5Padding
instead of
AES
but the Java server output an error:
should use AES.
How can i solve this issue?
For send on android side:
public void send(Bitmap mRgbImage1_array, int port_number)
throws IOException {
socket = new Socket("192.168.0.107", port_number);
boolean encrypt = HomeScreen.checkbox2.isChecked();
ByteArrayOutputStream stream = new ByteArrayOutputStream();
mRgbImage1_array.compress(CompressFormat.JPEG, 100, stream);
InputStream photoStream = new ByteArrayInputStream(stream.toByteArray());
BufferedInputStream bis = new BufferedInputStream(photoStream);
byte[] mybytearray = new byte[photoStream.available()];
bis.read(mybytearray, 0, mybytearray.length);
photoStream.close();
if(encrypt)
{
try {
byte[] dst = Security.encrypt(mybytearray);
mybytearray = new byte[10000];
for(int i=0; i<dst.length;i++)
{
mybytearray[i] = dst[i];
}
} catch (Exception e1) {
/// TODO Auto-generated catch block
e1.printStackTrace();
}
}
os = socket.getOutputStream();
os.write(mybytearray);
os.flush();
os.close();
if (os != null)
{
try {
os.close();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
For receive on Java server side:
public static void receive(int port_number) {
boolean received = false;
Socket socket = null;
InputStream is = null;
int bytesRead;
int current = 0;
BufferedOutputStream bos = null;
try {
if (serverSocket == null) {
serverSocket = new ServerSocket(port_number);
}
System.out.println("Listening :" + port_number);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
while (!received) {
try {
socket = serverSocket.accept();
InetAddress ip = socket.getInetAddress();
String[] ip1 = ip.toString().split("/");
ip2 = ip1[1];
System.out.println("ip is " + ip2);
byte[] mybytearray = new byte[10000000];
is = socket.getInputStream();
FileOutputStream fos = new FileOutputStream("source-image.jpeg");
bos = new BufferedOutputStream(fos);
bytesRead = is.read(mybytearray, 0, mybytearray.length);
current = bytesRead;
do {
bytesRead =
is.read(mybytearray, current, (mybytearray.length - current));
if (bytesRead >= 0) {
current += bytesRead;
}
} while (bytesRead > -1);
if(Networker.should_encrypt)
{
try {
mybytearray = Security1.decrypt(mybytearray);
} catch (Exception e1) {
/// TODO Auto-generated catch block
e1.printStackTrace();
}
}
bos.write(mybytearray, 0, current);
bos.flush();
bos.close();
received = true;
} catch (IOException ex) {
Logger.getLogger(MyServer1.class.getName()).log(Level.SEVERE, null, ex);
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
I believe this is the problem:
mybytearray = Security1.decrypt(mybytearray);
That's always going to decrypt 10000000 bytes, even if you've only actually written a small amount of data. You should change your decrypt method to say how much data to decrypt, then call doFinal(byte[], int, int).
I'd also suggest trying to handle the encryption/decryption in a streaming manner rather than preallocating 10MB (which is going to be wasteful in most cases, and could be too short in others), but that's a larger change.
Additionally, this is a bad idea in the encrypting code:
byte[] mybytearray = new byte[photoStream.available()];
bis.read(mybytearray, 0, mybytearray.length);
photoStream.close();
You're assuming that the amount available to start with is the whole file. That may not be the case. You should generally loop round, reading from the file stream and writing to an encrypting stream. Oh, and the close() call should be in a finally block.
If you really want to do all the encryption/decryption in one call, you can loop round reading from the file or network stream and writing into a ByteArrayOutputStream, so that you don't need to hard-code the size or assume it from available(). Then use ByteArrayOutputStream.toByteArray() to get a byte array of the right size. (That involves an extra copy, admittedly.)