I am trying to build a file transfer mechanism between 2 Java socket client. The sender client would include this sorta snippet:
FileInputStream fis = null;
BufferedInputStream bis = null;
BufferedOutputStream outStream = null;
byte[] fileBytes = new byte[(int) file.length()];
int bytesRead = 0;
try {
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
outStream = new BufferedOutputStream(socket.getOutputStream());
bytesRead = bis.read(fileBytes, 0, fileBytes.length);
outStream.write(fileBytes, 0, fileBytes.length);
} catch (IOException _IOExc) {
Logger.getLogger(ChatClient.class.getName()).log(Level.SEVERE,
null, _IOExc);
//QuitConnection(QUIT_TYPE_DEFAULT);
}
The server mediator would look like:
public void run() {
assert (outSocket != null);
byte[] bytes = new byte[fileSize];
try {
System.out.println("inStream " + inStream.available());
outStream = new BufferedOutputStream(outSocket.getOutputStream());
inStream.read(bytes, 0, fileSize);
outStream.write(bytes, 0, fileSize);
outStream.flush();
} catch (IOException ex) {
Logger.getLogger(FileTransport.class.getName()).log(Level.SEVERE,
null, ex);
}
}
the destination client:
public void run() {
try {
System.out.println("Start reading...");
int len = 1024;
BufferedInputStream inStream = new BufferedInputStream
(client.user.getClientSocket().getInputStream());
while ((bytesRead = inStream.read(fileBytes, 0, len)) >
0 && current < fileSize) {
current = current + bytesRead;
System.out.println("current "+ current);
bos.write(fileBytes, 0, bytesRead < len ? bytesRead : len);
}
bos.flush();
bos.close();
} catch (IOException ex) {
Logger.getLogger(ReadFileThread.class.getName()).log(Level.SEVERE,
null, ex);
} catch (InterruptedException e) {
}
}
Both the server and destination client is passed "fileSize" in advance, the problem now is server side get slight less data and the clientB keep reading only 8192 bytes of data from server and can never get out the loop.
Many thanks
Kev
Don't ignore the result of the read() method. It returns the number of bytes that have been read, which is not necessarily the length of the file. read() must always be called in a loop, until it returns -1.
And don't, ever, use available(). It doesn't return what you think it returns. Just loop until read() returns -1 or until the number of read bytes reaches the expected count.
Read the IO tutorial.
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.
I am currently working on a file transfer program and I ran into a strange issue.
I have two classes: A sender class and a recipient class. You can read the source code along with the error message below.
Thanks for your help in advance.
Sender:
public static void sendFile(final File file, final String ip){
Thread t = new Thread(new Runnable() {
#Override
public void run() {
try {
Socket s = new Socket(ip, 4816);
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
DataInputStream dis = new DataInputStream(s.getInputStream());
FileInputStream fis = new FileInputStream(file);
String filename = file.getName();
if(!dis.readUTF().equals("end")){
dos.writeUTF(filename);
dos.flush();
long size = file.length();
byte[] b = new byte[1024];
int read;
dos.writeUTF(Long.toString(size));
dos.flush();
while((read = fis.read(b)) != -1){
dos.write(b, 0, read);
dos.flush();
}
fis.close();
dos.flush();
dos.writeUTF("end");
System.out.println("Sender: Done");
dos.flush();
dis.close();
dos.close();
s.close();
}
return;
} catch (IOException e) {
e.printStackTrace();
}
}
});
t.start();
}
Recipient:
private ServerSocket sock;
private Thread t;
public listener(){
try {
sock = new ServerSocket(4816);
listen();
} catch (IOException e) {
e.printStackTrace();
}
}
private void listen(){
t = new Thread(new Runnable() {
public void run() {
Socket s;
try {
while((s = sock.accept()) != null){
DataInputStream dis = new DataInputStream(s.getInputStream());
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
String filename = dis.readUTF();
long size = Long.valueOf(dis.readUTF());
byte[] b = new byte[1024];
FileOutputStream fos = new FileOutputStream(new File(filename), true);
long read;
do{
read = dis.read(b, 0, b.length);
fos.write(b, 0, b.length);
}while(!(read < 1024));
System.out.println("Recipient: Done");
fos.close();
dos.close();
dis.close();
s.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
t.run();
}
Error (After 1086464 out of 1513308160 bytes were transmitted. [1062 * 1024]):
java.net.SocketException: Connection reset by peer: socket write error
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(Unknown Source)
at java.net.SocketOutputStream.write(Unknown Source)
at java.io.DataOutputStream.write(Unknown Source)
at main.sender$1.run(sender.java:60)
at java.lang.Thread.run(Unknown Source)
I think the issue is here in the recipient..
do{
read = dis.read(b, 0, b.length);
fos.write(b, 0, b.length);
}while(!(read < 1024));
You are saying to only loop while read is not less than 1024. Any read() operation can return a value less than the maximum buffer length at any time, even if the stream is not "at the end". Especially when network sockets are involved. The number of read bytes may be greater than 0 but less than 1024 on any read because that's simply how many bytes are available to the stream at that time.
The read call is giving you all the data it has (which fits the buffer) at that time, without having to block.
Try changing it to..
int read;
while ((read = dis.read(b, 0, b.length)) != -1 ) {
fos.write(b, 0, read);
}
You had this kind of loop in the sender and it is correct (although you don't need the flush within the loop).
This kind of bug is more common than you might think. I'v seen it a lot over the years, even in "enterprise products". It doesn't get picked up and fixed because most of the time it works... until it doesn't.
Another issue above is that you were always writing b.length bytes to the file, regardless of how many bytes had actually been read into the buffer.
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.
My server is sending several files to my client. However at the client side, it only receives the first file because I don't know how to iterate and get the second file.
The Server sends like this:
ListIterator iter = missingfiles.listIterator();
//missingfiles contain all the filenames to be sent
String filename;
while (iter.hasNext()) {
// System.out.println(iter.next());
filename=(String) iter.next();
File myFile = new File("src/ee4210/files/"+filename);
byte[] mybytearray = new byte[(int) myFile.length()];
FileInputStream fis = new FileInputStream(myFile);
BufferedInputStream bis = new BufferedInputStream(fis);
//bis.read(mybytearray, 0, mybytearray.length);
DataInputStream dis = new DataInputStream(bis);
dis.readFully(mybytearray, 0, mybytearray.length);
OutputStream os = _socket.getOutputStream();
//Sending file name and file size to the server
DataOutputStream dos = new DataOutputStream(os);
dos.writeUTF(myFile.getName());
dos.writeLong(mybytearray.length);
dos.write(mybytearray, 0, mybytearray.length);
dos.flush();
}
The client receives like this: (It will only receive the first file and I don't know how to make it loop to receive the next file)
int bytesRead;
int current = 0;
int filecount = 0;
InputStream in;
try {
in = _socket.getInputStream();
DataInputStream clientData = new DataInputStream(in);
String fileName = clientData.readUTF();
OutputStream output = new FileOutputStream(
"src/ee4210/files/"+ fileName);
long size = clientData.readLong();
byte[] buffer = new byte[1024];
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();
}
How to receive multiple files from outputstream?
The obvious answer is 'one at a time, with extra information to tell you where one stops and another starts'. The most usual technique is to send the file size ahead of the file, e.g. as a long via DataOutputStream.writeLong(), and at the receiver change your read loop to stop after exactly that many bytes, close the output file, and continue the outer loop that reads the next long or end-of-stream.
You can try this.
I've used a lazy method to check that the end of all 3 files have been received.
int bytesRead;
int current = 0;
int filecount = 0;
InputStream in;
try
{
in = _socket.getInputStream();
DataInputStream clientData = new DataInputStream(in);
while(true)
{
String fileName = clientData.readUTF();
// will throw an EOFException when the end of file is reached. Exit loop then.
OutputStream output = new FileOutputStream("src/ee4210/files/"+ fileName);
long size = clientData.readLong();
byte[] buffer = new byte[1024];
while (size > 0
&& (bytesRead = clientData.read(buffer, 0,
(int) Math.min(buffer.length, size))) != -1)
{
output.write(buffer, 0, bytesRead);
size -= bytesRead;
}
output.close();
}
}
catch (EOFException e)
{
// means we have read all the files
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}