Java Socket Corrupting PNG-image - java

I am currently trying to use a Socket to send a PNG or JPEG image from one Client to another (in Java) but the images always becomes corrupted (when I try to open it it just says that it can't be opened because it's damaged, faulty or too big).
I have tried the methods that load the images into byte[] and if I just load an image into a byte[] and then save it back down it works perfectly so the problem must be in the sending of the byte[].
Here are the functions I use for the sending:
/**
* Attempts to send data through the socket with the BufferedOutputStream. <p>
* Any safety checks should be done beforehand
* #param data - the byte[] containing the data that shall be sent
* #return - returns 'true' if the sending succeeded and 'false' in case of IOException
*/
public boolean sendData(byte[] data){
try {
//We simply try to send the data
outS.write(data, 0, data.length);
outS.flush();
return true; //Success
} catch (IOException e) {
e.printStackTrace();
return false; //Failed
}
}
/**
* Attempts to receive data sent to the socket. It uses a BufferedInputStream
* #param size - the number of bytes that should be read
* #return - byte[] with the received bytes or 'null' in case of an IOException
*/
public byte[] receiveData(int size){
try {
int read = 0, r;
byte[] data = new byte[size];
do{
//We keep reading until we have gotten all data
r = inS.read(data, read, size-read);
if(r > 0)read += r;
}while(r>-1 && read<size); //We stop only if we either hit the end of the
//data or if we have received the amount of data we expected
return data;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
The images that arrive seems to be the correct size and all so the data is at least arriving, just corrupted.

Throw your receiveData() method away and use DataInputStream.readFully().

Related

Stream isn't closing after sending file

Excuse me my understanding of streams is slowly building.
I initally tried to stream a music file over to my client but that didn't work out so I moved transferring the entire file via bytes then saving it at the client side. The problem is the input stream is still receiving bytes and so won't break out of the while loop (Where I want to play the song)
Below is part of my client code:
//This is part of a task that carries this out (Part of a UI application)
#Override
protected Void call()
{
try(Socket socket = new Socket(host,portNumber);
ObjectOutputStream toServer = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream fromServer = new ObjectInputStream(socket.getInputStream()))
{
//Client requests a song from the server (song is a String)
toServer.writeUTF(".Music."+song);
toServer.flush();
//Create a new file (tempSong is a string)
File test = new File(tempSong);
test.createNewFile();
//New file writer
BufferedOutputStream bOS = new BufferedOutputStream(new FileOutputStream(test));
byte[] buffer = new byte[4096];
int current;
/**
* Read the bytes from the server and write the file
* The file is written and I can play it (externally)
* but the while loop doesn't break after writting the file
*/
while ((current = fromServer.read(buffer)) > 0)
{
bOS.write(buffer, 0 , current);
}
System.out.println("Finished writing");
bOS.close();
/**
* down here a method is ran to play the file
* but it never happen because the task is still in the while loop
*/
}
catch (IOException e)
{
e.printStackTrace();
}
return null;
}
below is in the server where the server reads the message and sends the file
/** This is part of a task and the main call area */
#Override
public Void call ()
{
try
{
//Setup I/O
toClient = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream fromClient = new ObjectInputStream(socket.getInputStream());
while(!socket.isClosed())
{
//If server has received a message
if(fromClient.available() > 0)
{
//Reads message and objects from client
String input = fromClient.readUTF();
if (input.contains(".Music"))
{
findMusic(input, toClient);
}
/**
* more else IFs
*/
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
/**
* this method is part of the task discussed above
*/
//The method that is called
private void findMusic(String input, ObjectOutputStream toClient)
{
logoff();
String[] names = input.split("[.]");
clientManagerTemp.logger("Someone request song: " + names[2] + ".mp3");
File musicFile = AudioUtil.getSoundFile("src/Resources/Songs/" + names[2]+ ".mp3");
byte[] buffer = new byte[(int) musicFile.length()];
try(BufferedInputStream bis = new BufferedInputStream(new FileInputStream(musicFile)))
{
bis.read(buffer, 0, buffer.length);
clientManagerTemp.logger("Sending " + "src/Resources/Songs/" + names[2]+ ".mp3" + "(" + buffer.length + " bytes)");
//write the file to the client
toClient.write(buffer,0, buffer.length);
toClient.flush();
clientManagerTemp.logger("Finished sending");
}
catch (IOException e)
{
e.printStackTrace();
}
}
So as you can see the server sends the file fine and my client receives it. It just won't stop the while loop. Can someone explain why? So I can get a better understanding of how streaming bytes over sockets work
**EDIT
When the client receives the file it can be played even after closing the client and server
You can send the file-length to the client, with this information the client knows when to exit the loop.
Server
private void findMusic(String input, ObjectOutputStream toClient)
{
logoff();
String[] names = input.split("[.]");
clientManagerTemp.logger("Someone request song: " + names[2] + ".mp3");
File musicFile = AudioUtil.getSoundFile("src/Resources/Songs/" + names[2]+ ".mp3");
int fileLength = (int) musicFile.length();
byte[] buffer = new byte[fileLength];
try(BufferedInputStream bis = new BufferedInputStream(new FileInputStream(musicFile)))
{
bis.read(buffer, 0, buffer.length);
clientManagerTemp.logger("Sending " + "src/Resources/Songs/" + names[2]+ ".mp3" + "(" + buffer.length + " bytes)");
//write the file to the client
toClient.writeInt(fileLength);
toClient.write(buffer,0, buffer.length);
toClient.flush();
clientManagerTemp.logger("Finished sending");
}
catch (IOException e)
{
e.printStackTrace();
}
}
Client
byte[] buffer = new byte[4096];
int current;
int fileLength = fromServer.readInt();
while ( fileLength > 0 && (current = fromServer.read(buffer, 0, Math.min(4096,fileLength))) > 0)
{
bOS.write(buffer, 0 , current);
fileLength -= current;
}
This is due to the nature of the read method, seen here Javadocs. This method will block until there is data, so your loop will never end. Now the reason for this is because you never close the stream on the server end, you just flush it, which forces a send of all the data currently in the buffer, but will not close the stream. If you call .close() on the stream from the server side then that should exit the while loop on the client side and continue to your playing code.
I haven't tested this, but from the documentation and a brief look at your code, that seems to be the issue.
If you don't close the connection (server side), there will not be an end-of-file/end-of-stream and your client side will run the loop forever, unless you configured a read timeout.
If you won't to close the loop automatically after sending the file is finished, send the count of bytes (size of file) first to client and then the file itself. This makes it possible to read only a exact number of bytes on client side and close the connection when finished.
Alternative you can send a special sequence and check for them at client side to mark the end of stream.
The reason you program is stuck is due to the behaviour of ObjectInputStream.read(). This method is blocking until it has read some data or the stream is closed. Since you never close the stream the 2nd condition will never be met. So there is only option 1. To return when data arrived. Unfortuenatly there is no way to recognize the end of a file unless you define it yourself e.g. by sending a unique sequenz or something that you can recognize to break the while loop.
Example:
while ((current = fromServer.read(buffer)) > 0)
{
// example value 42 could be anything else as well
if(current == -42)
{
break;
}
bOS.write(buffer, 0 , current);
}
The downside of this is, that your data could also contain this special value so you gotta think about different possibilities e.g memorize the sequence up to 3 values and compare them to an 'interrupt sequence'.

use FileInputStream and FileOutputStream as a buffer

I would like to use the hard drive as a buffer for audio signals. My Idea was to just write the samples in byte form to a file, and read the same with another thread. However, I have to problem that FIS.read(byte[]); returns 0 and gives me an empty buffer.
What is the problem here?
This is my operation for writing bytes:
try {
bufferOS.write(audioChunk);
bufferOS.flush();
} catch (IOException ex) {
//...
}
And this is what my reader does:
byte audioChunk[] = new byte[bufferSize];
int readBufferSize;
int freeBufferSize = line.available(); // line = audioline, available returns free space in buffer
try {
readBufferSize = bufferIS.read(audioChunk,freeBufferSize, 0);
} catch(IOException e) {
//...
}
I create both bufferOS and bufferIS with the same file, both work.
The writer works, the file gets created and has the correct data in it.
However the bufferIS.read();-call always returns 0.
The fileInputStream returns the correct amount of available bytes with buffer.available(); and parameters like freeBufferSize and audioChunk.length are correct.
Is there a problem with running FileInputStream and FileOutputStream on the same file in windows?
You're passing the arguments in the wrong order to the read call, it should be:
readBufferSize = bufferIS.read(audioChunk, 0, freeBufferSize);
Right now you're passing freeBufferSize as the offset to store the result of the read call and 0 as the number of bytes to read at maximum. It's not surprising that, if you tell the read call to read at most zero bytes, that it returns that it has read zero bytes.
Javadoc:
* #param b the buffer into which the data is read.
* #param off the start offset in array <code>b</code>
* at which the data is written.
* #param len the maximum number of bytes to read.
* #return the total number of bytes read into the buffer, or
* <code>-1</code> if there is no more data because the end of
* the stream has been reached.
public abstract class InputStream implements Closeable {
// ....
public int read(byte b[], int off, int len) throws IOException

InputStream receive method blocking

I am stuck with the following problem. I have created a connection to a remote echo server. The following method is used for receiving the bytes received from the server:
public byte[] receive() {
byte[] resultBuff = new byte[0];
byte[] buff = new byte[4096];
try {
InputStream in = socket.getInputStream();
int k = -1;
while((k = in.read(buff, 0, buff.length)) != -1) {
System.out.println(k);
byte[] tbuff = new byte[resultBuff.length + k]; // temp buffer size = bytes already read + bytes last read
System.arraycopy(resultBuff, 0, tbuff, 0, resultBuff.length); // copy previous bytes
System.arraycopy(buff, 0, tbuff, resultBuff.length, k); // copy current lot
resultBuff = tbuff; // call the temp buffer as your result buff
String test = new String(resultBuff);
System.out.println(test);
}
System.out.println(resultBuff.length + " bytes read.");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return resultBuff;
}
I am able to get the following response from the server:
Connection to MSRG Echo server established
The problem is that the loop gets stuck at the second execution on in.read(). I understand that this is due the the server not sending any EOF info and the like.
I am not sure which of the following two solutions is correct and in which way to implement it:
Each message coming from the server will be read by a new execution of the receive() method. How do I prevent the in.read() method from blocking?
The loop inside the receive() method should be kept alive until application exit. This means that my implementation is currently using in.read() wrong. In which way should this be implemented.
The key to this question is your use of the word 'message'. There are no messages in TCP, only a byte stream. If you want messages you must implement them yourself: read a byte at a time until you have a complete message, process it, rinse and repeat. You can amortize the cost of the single-byte reads by using a BufferedInputStream.
But there are no messages in an echo server. Your read and accumulate strategy is therefore inappropriate. Just echo immediately whatever you received.

I keep getting a UTF error when transferring image data as bytes over a socket

I'm trying to send images from my computer to my phone by using a Java program connect to an Android app.
The code I use to send the image:
byte[] data = imageToByteArray(img);
sendMessage(Main.imageCheck + data.length);
out.write(data);
out.flush();
This code is run from the Java program on my computer. The sendMessage() method is:
public void sendMessage(String s) {
if (out != null && server.isConnected()) {
try {
out.writeUTF(s);
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
The code I use to receive the image and other data:
while ((input = in.readUTF()) != null) {
if (!input.equalsIgnoreCase(pulseCheckMessage)) {
if (input.contains(imageCheck)) {
//byte[] data = Base64.decode(input.replace(imageCheck, ""), Base64.DEFAULT);
byte[] data = new byte[Integer.parseInt(input.replace(imageCheck, ""))];
int length = in.read(data);
updateScreenImage = BitmapFactory.decodeByteArray(data, 0, length);
MainActivity.this.runOnUiThread(updateUiRunnable);
} else {
inLine = "Received: " + input;
MainActivity.this.runOnUiThread(updateUiRunnable);
}
}
pulseTakenTime = System.currentTimeMillis();
pulseCheckReceived = true;
isAlive = true;
}
The commented out code (including the base64 class) was when I was sending the image data as a string by using Base64.encode to convert the byte[] array to a string to send over, which worked without a problem except that it was too slow. So I decided to send the bytes over which I thought would be faster.
imageCheck and Main.imageCheck are strings that are the same. The app receives that string and recognizes it and expects a byte[] array so it tries to do in.read(data) however I get an error:
ERROR: java.nio.charset.ModifiedUtf8.decode(ModifiedUtf8.java:57)
ERROR: java.io.DataInputStream.decodeUTF(DataInputStream.java:444)
ERROR: java.io.DataInputStream.decodeUTF(DataInputStream.java:438)
ERROR: java.io.DataInputStream.readUTF(DataInputStream.java:433)
ERROR: com.mangopearapples.pccontroller.MainActivity.run(MainActivity.java:238)
Thanks.
My guess is that int length = in.read(data); doesn't read all the bytes that have been sent, as documented:
Reads some number of bytes from the contained input stream and stores them into the buffer array b. The number of bytes actually read is returned as an integer
(emphasis mine)
So, the next iteration tries to read the unread bytes of the image as UTF, and it fails. Always use a loop to read bytes from a stream, which stops once all the expected number of bytes have been read.
To confirm that this is the real problem, debug and see if the length returned matches with the actual length.

android can not receive all data sent from pc

In my program, pc sends data to my android phone continuously. Each time pc send 62 bytes(24 for head and 38 for content). My program runs well. But sometimes my phone can read 24 bytes for head and only 10 bytes for content.
I receive data via a non-block socketchannel.
private SocketChannel client = null;
public int read(byte[] data,int offset, int len){
try {
ByteBuffer buffer = ByteBuffer.allocate(len);
int read_len = client.read(buffer);
if(read_len == 0){
}else if(read_len == -1){
}else{
buffer.flip();
buffer.get(data, offset, read_len);
}
return read_len;
} catch (IOException e) {
e.printStackTrace();
}
return -2;
}
How can I solve this problem?
But sometimes my phone can read 24 bytes for head and only 10 bytes for content.
It's possible there is a network problem, but I think it's more likely that since TCP is a stream oriented protocol, which does not preserve message boundaries, you are occasionally getting payloads that do not correspond with the bytes sent by the pc. This is OK and expected. It just means you have to do a bit more work.
I assume that with your read message you want to keep reading data until you have enough data to fill the supplied byte array. Try something this:
public int read(byte[] data, int offset, int len)
{
try
{
ByteBuffer buffer = ByteBuffer.allocate(len - offset);
while (buffer.hasRemaining())
{
int read_len = client.read(buffer);
if (read_len == -1)
return -1;
}
buffer.flip();
int result = buffer.remaining();
buffer.get(data, offset, buffer.remaining());
return result;
}
catch (IOException e)
{
e.printStackTrace();
}
return -2;
}
The nice thing about reading into a byte buffer is that it takes care of concatenating the bytes correctly on multiple reads, so just loop until full.
BTW - don't forget to subtract the offset (in case it is non-zero) from the length of your buffer.

Categories

Resources