I'm trying to send an image file from a server to a client via a socket. The socket was previously used to send some strings from the server to the client (with buffered input/output streams).
The trouble is the image file can't be received properly, with "Premature end of JPEG file" error.
The server first sends the file size to the client, the client then creates a byte[] of that size, and starts to receive the file.
Here are the codes:
Server:
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
//Send file size
dos.writeInt((int) file.length());
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
byte[] fileBytes = new byte[bis.available()];
bis.read(fileBytes);
bis.close();
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
bos.write(fileBytes);
bos.flush();
Client:
DataInputStream dis = new DataInputStream(socket.getInputStream());
//Receive file size
int size = dis.readInt();
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
byte[] fileBytes = new byte[size];
bis.read(fileBytes, 0, fileBytes.length);
More interestingly, if I let server sleep for about 2 seconds between sending the file size and writing the byte[], then the image is received properly. I wonder if there's some kind of race condition between the server and the client
The error is most likely here:
byte[] fileBytes = new byte[bis.available()];
The method available does not return the size of the file. It might return only the size of the input buffer, which is smaller than the size of the file. See the API documentation of the method in BufferedInputStream.
Also, read in the line below is not guaranteed to read the whole file in one go. It returns the number of bytes that were actually read, which can be less than what you asked for. And in the client code, you are using read in the same way, without actually checking if it read all the data.
Please check commons-io with FileUtils and IOUtils. This should make work a lot easier.
http://commons.apache.org/io/
The correct way to copy a stream in Java is as follows:
int count;
byte[] buffer = new byte[8192]; // more if you like, but over a network it won't make much difference
while ((count = in.read(buffer)) > 0)
{
out.write(buffer, 0, count);
}
Your code fails to logically match this at several points.
Also available() is not a valid way to determine either a file size or the size of an incoming network transmission - see the Javadoc. It has few if any correct uses and these aren't two of them.
Related
I'm trying to write an HTTP proxy in Java using only the Socket class. I had attempted to construct one earlier, and I was successfully sending a request by writing to the socket's output stream But I am having a hard time reading the response. the research I have conducted suggests that I should use the input stream and read it line by line, but I have not been able to read any web-pages successfully using this method. Would anyone have any suggestions as to where I could go from here?
My code actually uses a byte buffer to read from the input stream in order to read the page in bytes:
InputStream input = clientSocket.getInputStream()
byte[] buffer = new byte[48*1024];
byte[] redData;
StringBuilder clientData = new StringBuilder();
String redDataText;
int red;
while((red = input.read(buffer)) > -1) {
redData = new byte[red];
System.arraycopy(buffer, 0, redData, 0, red);
redDataText = new String(redData, "UTF-8");
System.out.println("Got message!! " + redDataText);
clientData.append(redDataText);
}
If you are asking for a way to read an InputStream by lines, this one may serve you:
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(input, "UTF-8"));
String line;
StringBuilder clientData=new StringBuilder();
while ((line=bufferedReader.readLine()) != null)
{
clientData.append(line);
}
You have to be careful not to read an InputStream in this fashion unless you are a priori sure that it contains just plain text (and not binary data).
BTW: For shake of efficiency, I recommend you to pre-size the clientData with an initial size according to the final size (if not, it will start from a default size of 10, and will need to be re-sized more times).
This question already has answers here:
Java multiple file transfer over socket
(3 answers)
Closed 7 years ago.
I have a file sender and receiver. From the 10 or so posts I've found on the internet, this is the correct way to send files through sockets in Java. Take a look at the code snippets below.
Sender:
OutputStream bos = clientSocket.getOutputStream();
FileInputStream fis = new FileInputStream(sendingFile);
BufferedInputStream bis = new BufferedInputStream(fis);
int n;
int total = 0;
byte[] buffer = new byte[8192];
while ((n = bis.read(buffer)) > 0) {
bos.write(buffer, 0, n);
System.out.println("Sending File... ");
total += n;
System.out.println(total);
}
Receiver:
InputStream bis = clientSocket.getInputStream();
FileOutputStream fos = new FileOutputStream(fileOut);
BufferedOutputStream bos = new BufferedOutputStream(fos);
int n;
int total = 0;
byte[] buffer = new byte[8192];
while ((n = bis.read(buffer)) > -1) {
bos.write(buffer, 0, buffer.length);
System.out.println("Writing File... ");
total += n;
System.out.println(total);
if (total == fileSize) {
break;
}
}
System.out.println("File Received");
Here's the output from the Sender
Sending File: D:\Users\Administrator\Documents\AAAAAAAAAAAAAAAAAAAAAAAAAAA.avi
File Size: 54236160
Sending File...
8192
Sending File...
16384
....................some time later
Sending File...
54231040
Sending File...
54236160
File Sent
The last number, 54236160, is the exact size of the file being sent. Now here is the last line on the receiver side.
Writing file...
54234710
So for this particular file, it always stops at this size and so because the entire file isn't sent, the receiver never stops waiting for data. I dont understand how the sender is sending the correct amount of data but the receiver doesn't get it all. I never see "File Received" on the receiver side and the amount of data that the receiver reads is never equal to the amount being sent.
This issue occurs for any file I sent and if I send really small files, such as those that are in bytes not kb, I dont see any "Writing File..." on the receiver side at all, almost like it just ignores the stream.
Yet every post on the internet says this is the proper way to send files. Oh and if I close the sending socket and/or stream, which I dont want to do because there are other things the client and server need to do, it still doesnt fix the problem anyway.
The other thing I've noticed is that while the sender always seems to indicate its writing the full amount to the buffer (always multiples of 8192 for "total"), the receiver doesnt.
Below is the code that gets the filesize and filename. Perhaps this is where the error lies? I dont see how since its all done before we start sending and receiving the file.
System.out.println("Receiving File");
try {
clientScanner.nextLine();
String fileName = clientScanner.nextLine();
int fileSize = clientScanner.nextInt();
System.out.println("File: " + fileName + " Size: " + fileSize);
File fileOut = new File("C:\\Users\\owner\\AppData\\Local\\temp\\" + fileName);
InputStream bis = clientSocket.getInputStream();
FileOutputStream fos = new FileOutputStream(fileOut);
BufferedOutputStream bos = new BufferedOutputStream(fos);
You shouldn't try to read more data than expected. Your loop should be:
int n;
int bytesLeft = fileSize;
byte[] buffer = new byte[8192];
while (bytesLeft > 0) {
int n = bis.read(buffer, 0, Math.min(buffer.length, bytesLeft));
if (n < 0) {
throw new EOFException("Expected " + bytesLeft + " more bytes to read");
}
bos.write(buffer, 0, n);
System.out.println("Writing File... ");
bytesLeft -= n;
System.out.println(total " bytes left to read");
}
(You can adjust the output to show how many bytes you've read instead of how many you've got left to read if you want, but I think this approach is simpler.)
If that still doesn't get you all the data, you should flush the output stream in the writing code - I wouldn't expect you'd have to, but if you're not closing the socket, I guess it could be buffering it forever.
If the loop terminates but the file appears to be incomplete, make sure you're closing bos after all of this.
If none of that helps, then it's possible that something else has read the start of your data before you get into this loop. You should compare the data in the output file with the original file. Look at the first (say) 16 bytes in the output file, and check that they're at the start of the input file. If they're not, then that suggests that the problem is with what you're doing with the connection before the code you've shown us. (For example, the code used to send and receive the expected file size.)
Now that we can see how you're sending / receiving the file size, that's definitely the problem. The scanner is reading some of the file data. To fix this, just use DataOutputStream on the sending side and DataInputStream on the receiving side:
DataOutputStream output = new DataOutputStream(clientSocket.getOutputStream());
output.writeUTF(fileName);
output.writeInt(fileSize);
// Write the data as before, to output
On the receiving side:
DataInputStream input = new DataOutputStream(clientSocket.getInputStream());
String fileName = input.readUTF(name);
int fileSize = input.readInt();
// Read the data before, as above, from input
i have to send a short string as text from client to server and then after that send a binary file.
how would I send both binary file and the string using the same socket connection?
the server is a java desktop application and the client is an Android tablet. i have already set it up to send text messages between the client and server in both directions. i have not yet done the binary file sending part.
one idea is to set up two separate servers running at the same time. I think this is possible if i use two different port numbers and set up the servers on two different threads in the application. and i would have to set up two concurrent clients running on two services in the Android app.
the other idea is to somehow use an if else statement to determine which of the two types of files is being sent, either text of binary, and use the appropriate method to receive the file for the file type being sent.
example code for sending text
PrintWriter out;
BufferedReader in;
out = new PrintWriter(new BufferedWriter
(new OutputStreamWriter(Socket.getOutputStream())) true,);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out.println("test out");
String message = in.readLine();
example code for sending binary file
BufferedOutputStream out;
BufferedInputStream in;
byte[] buffer = new byte[];
int length = 0;
out = new BufferedOutputStream(new FileOutputStream("test.pdf));
in = new BufferedInputStream(new FileOutputStream("replacement.pdf"));
while((length = in.read(buffer)) > 0 ){
out.write(buffer, 0, length);
}
I don't think using two threads would be necessary in your case. Simply use the socket's InputStream and OutputStream in order to send binary data after you have sent your text messages.
Server Code
OutputStream stream = socket.getOutputStream();
PrintWriter out = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(stream)
)
);
out.println("test output");
out.flush(); // ensure that the string is not buffered by the BufferedWriter
byte[] data = getBinaryDataSomehow();
stream.write(data);
Client Code
InputStream stream = socket.getInputStream();
String message = readLineFrom(stream);
int dataSize = getSizeOfBinaryDataSomehow();
int totalBytesRead = 0;
byte[] data = new byte[dataSize];
while (totalBytesRead < dataSize) {
int bytesRemaining = dataSize - totalBytesRead;
int bytesRead = stream.read(data, totalBytesRead, bytesRemaining);
if (bytesRead == -1) {
return; // socket has been closed
}
totalBytesRead += bytesRead;
}
In order to determine the correct dataSize on the client side you have to transmit the size of the binary block somehow. You could send it as a String right before out.flush() in the Server Code or make it part of your binary data. In the latter case the first four or eight bytes could hold the actual length of the binary data in bytes.
Hope this helps.
Edit
As #EJP correctly pointed out, using a BufferedReader on the client side will probably result in corrupted or missing binary data because the BufferedReader "steals" some bytes from the binary data to fill its buffer. Instead you should read the string data yourself and either look for a delimiter or have the length of the string data transmitted by some other means.
/* Reads all bytes from the specified stream until it finds a line feed character (\n).
* For simplicity's sake I'm reading one character at a time.
* It might be better to use a PushbackInputStream, read more bytes at
* once, and push the surplus bytes back into the stream...
*/
private static String readLineFrom(InputStream stream) throws IOException {
InputStreamReader reader = new InputStreamReader(stream);
StringBuffer buffer = new StringBuffer();
for (int character = reader.read(); character != -1; character = reader.read()) {
if (character == '\n')
break;
buffer.append((char)character);
}
return buffer.toString();
}
You can read about how HTTP protocol works which essentially sends 'ascii and human readable' headers (so to speak) and after that any content can be added with appropriate encoding like base64 for example. You may create sth similar yourself.
You need to first send the String, then the size of the byte array then the byte array, use String.startsWith() method to check what is being send.
I want to stream and audio with SIP Connection in java application(SE).I connected with the server and got 200 OK messages.I want to receive data sent by the server. I created a SOCKET and got an InputStream. Here is how I do it. 123.456.789.1 is the my ip address and 1234 is which my application listening port.
Socket socket=new Socket("123.456.789.1",1234);
InputStream in=socket.getInputStream();
System.out.println("inputSream available :"+in.available());
But in.available() is always 0 .
But if I get the Object content=response.getContent();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutput out = new ObjectOutputStream(bos);
out.writeObject(content);
byte[] contentBytes = bos.toByteArray();
the lenght of contenBytes equals to the response content length.But when I try to get inputStream and Play ,like following
InputStream pp=new ByteArrayInputStream(b);
AudioStream as = new AudioStream(pp);
AudioData data = as.getData();
ContinuousAudioDataStream cas = new ContinuousAudioDataStream (data);
An Exception throws;java.io.IOException: could not create audio stream from input stream
Then I tried to read the inputstream in.read() then when read some bytes,and IOException was thrown.
Q1. How can I solve and get InputStream from the socket?
Q2. how to get an inputStream to play the audio?
or let me know where the problem is and how to solve it.
UPDATED: Thank you all who showed a fault in.availabe();
Then I changed the code.
ByteArrayOutputStream ou=new ByteArrayOutputStream();
int i=0;
System.out.println("Before while");
while((i=in.read())!=-1){
ou.write(i);
System.out.println("Wrote :"+i);
}
Unfortunately the application doesn't go further.That means only Before while is printed.Application just shows running(I use netbeans IDE).I don't why.Any clarification?
When you use getContent you get some kind of object wrapping the content. Then using an ObjectOutputStream you write the Java representation of that object, not the actual bytes of the original data.
You should be able to do
AudioStream as = new AudioStream(in);
AudioData data = as.getData();
ContinuousAudioDataStream cas = new ContinuousAudioDataStream (data);
or if you do want to buffer the data
int chunkSize;
byte[] chunk = new byte[2048];
ByteArrayOutputStream outBuffer = new ByteArrayOutputStream();
while ( ( chunkSize = in.read(chunk) ) != -1) {
outBuffer.write(chunk, 0, chunkSize);
}
ByteArrayInputStream inBuffer = new ByteArrayInputStream(outBuffer.toByteArray());
AudioStream as = new AudioStream(inBuffer);
AudioData data = as.getData();
ContinuousAudioDataStream cas = new ContinuousAudioDataStream (data);
available() show how many bytes can be guaranteed read before blocking. It might always return 0.
available() is the number of bytes which can be read with out performing a blocking call to the OS. If you want to know how much data is available you should try to read it and see how much you get.
my assignment includes sending an image file using UDP service (using java I implemented that successfully). My professor asked to include:
"The exchanged data messages must also have a header part for the sender to include 16-bit message sequence number for duplicate filtering at the receiver end"
How to do this?
I assume to create your UDP packet, you are using a ByteArrayOutputStream to generate the data. If that is the case, just Wrap a DataOutputStream on top of that ByteArrayOutputStream, and call writeInt(somesequenceNumber) before writing the image data to the stream.
on the receive side, do the opposite, wrap a DataInputStream around a ByteArrayInputStream, and call readInt() to get the sequence number. From there you can check whether you have already received this packet.
Something like
Write Side
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeInt(sequenceNumber++);
dos.writeInt(imageDataLength);
dos.write(imageData);
dos.flush();
byte[] udpPacketBytes = baos.toByteArray();
Read Side
ByteArrayInputStream bais = new ByteArrayInputStream(udpPacketBytes);
DataInputStream dis = new DataInputStream(bais);
int sequenceNumber = dis.readInt();
if (seenSequenceNumbers.add(Integer.valueOf(sequenceNumber)))
{
int imageLength = dis.readInt();
byte[] imageData = new byte[imageLength];
dis.read(imageData);
}
where seenSequenceNumbers is some Set
For a 16-bit value I would use DataOutputStream.writeShort() and DataInputSTream readShort()/readUnsignedShort(). writeInt() and readInt() are for 32-bit values. If you want to avoid duplicates, a 32-bit value may be a better choice in any case. ;)