Jar File is corrupt after sending over socket - java

So i am building a program which needs an auto-updating feature built in to it, as i was finished up and tested it out, it seems when i send the jar file over the socket and write it to the newly made jar file it is missing 5KB (everytime... even when the size changes) size from it and becomes corrupt.
Here is my code:
package server.update;
import java.io.*;
import java.net.Socket;
public class UpdateThread extends Thread
{
BufferedInputStream input; //not used
BufferedInputStream fileInput;
BufferedOutputStream output;
public UpdateThread(Socket client) throws IOException
{
super("UpdateThread");
output = new BufferedOutputStream(client.getOutputStream());
input = new BufferedInputStream(client.getInputStream());
}
public void run()
{
try
{
File perm = new File(System.getProperty("user.dir")+"/GameClient.jar");
//fileInput = new BufferedInputStream(new FileInputStream(perm));
fileInput = new BufferedInputStream(new FileInputStream(perm));
byte[] buffer = new byte[1024];
int numRead;
while((numRead = fileInput.read(buffer)) != -1)
output.write(buffer, 0, numRead);
fileInput.close();
input.close();
output.close();
this.interrupt();
}
catch(Exception e)
{e.printStackTrace();}
}
}
This is the class that will wait for a connection from the client and then push the update to them as soon as it connects. File Perm is the jar file that i want to send over and for whatever reason it seems to either miss the last 5 bytes or the client doesn't read the last 5 (i don't know which). Here is the client's class of receiving the information here:
public void getUpdate(String ip) throws UnknownHostException, IOException
{
System.out.println("Connecting to update socket");
update = new Socket(ip,10004);
BufferedInputStream is = new BufferedInputStream(update.getInputStream());
BufferedOutputStream os = new BufferedOutputStream(update.getOutputStream());
System.out.println("Cleaning GameClient.jar file");
File updated = new File(System.getProperty("user.dir")+"/GameClient.jar");
if(updated.exists())
updated.delete();
updated.createNewFile();
BufferedOutputStream osf = new BufferedOutputStream(new FileOutputStream(updated));
System.out.println("Writing to GameClient.jar");
byte[] buffer = new byte[1024];
int numRead = 0;
while((numRead = is.read(buffer)) != -1)
osf.write(buffer, 0, numRead);
System.out.println("Finished updating...");
is.close();
os.close();
update.close();
osf.close();
}
Any help is appreciated. Thanks!

You have too many closes. Remove update.close() and is.close(). These both close the socket, which prevents the buffered stream 'osf' from being auto-flushed when closed. Closing either the input stream or the output stream or a socket closes the other stream and the socket. You should therefore only close the outermost output stream you have wrapped around the socket, in this case osf, and maybe the socket itself in a finally block to be sure.

Thanks to MDR for the answer, it worked!!
I had to change the following lines of code in the UpdateThread class:
Before:
fileInput.close();
input.close();
output.close();
this.interrupt();
After:
fileInput.close();
output.flush();
output.close();
input.close();
this.interrupt();
You must flush the stream before closing, also i switched the order because if you closed the inputstream attached to the socket it will close the socket and then will not move on to closing the outputstream or flushing it.
Thanks again!

Have you considered using an http library to delegate all of the connection handling and reading/writing to known working code? You're reinventing a lot of wheels here. Additionally at some point you're going to want to ensure the content you're receiving is authentic and undamaged (you're doing that by loading the class, which is somewhat dangerous, especially when you're exchanging data in cleartext!) Again, using a library and its methods would allow you to choose HTTPS, allowing TLS to do much of your work.
I'd also suggest that your server tell the client some metadata in advance, regardless- perhaps the content length and possibly a hash or checksum so the client can detect failures in the transfer.
This question seems to have answers relevant to your situation as well. Good luck!

Related

Reading multiple files in loop from FTP server using Apache Commons Net FTPClient

I have list of files that needs to be read from FTP server.
I have a method readFile(String path, FTPClient client) which reads and prints the file.
public byte[] readFile(String path,FTPClient client){
InputStream inStream = null;
ByteArrayOutputStream os = null;
byte[] finalBytes = new byte[0];
int reply;
int len;
byte[] buffer = new byte[1024];
try{
os = new ByteArrayOutputStream();
inStream = client.retrieveFileStream(path);
reply = client.getReplyCode();
log.warn("In getFTPfilebytes() :: Reply code -"+reply);
while ((len = inStream.read(buffer)) != -1) {
// write bytes from the buffer into output stream
os.write(buffer, 0, len);
}
finalBytes = os.toByteArray();
if(inStream == null){
throw new Exception("File not found");
}
inStream.close();
}catch(Exception e){
}finally{
try{ inStream.close();} catch(Exception e){}
}
return finalBytes;
}
I am calling above method in loop of list which contains strings of file path.
Issue - In loop only first file is getting read properly. Afterwards, it does not read file and throws an exception. inStream gives NULL for second iteration/second file. Also while iterating first file reply code after retrieveFileStream is "125(Data connection already open; transfer starting.)"
In second iteration it gives "200 (The requested action has been successfully completed.)"
I am not able to understand what is wrong here.
Have not closing inputstream connection properly?
You have to call FTPClient.completePendingCommand and close the input stream, as the documentation for FTPClient.retrieveFileStream says:
Returns an InputStream from which a named file from the server
can be read. If the current file type is ASCII, the returned
InputStream will convert line separators in the file to
the local representation. You must close the InputStream when you
finish reading from it. The InputStream itself will take care of
closing the parent data connection socket upon being closed.
To finalize the file transfer you must call completePendingCommand and
check its return value to verify success.
If this is not done, subsequent commands may behave unexpectedly.
inStream = client.retrieveFileStream(path);
try {
while ((len = inStream.read(buffer)) != -1) {
// write bytes from the buffer into output stream
os.write(buffer, 0, len);
}
finalBytes = os.toByteArray();
} finally {
inStream.close()
if (!client.completePendingCommand()) {
// error
}
}
Btw, there are better ways for copying from InputStream to OutputStream:
Easy way to write contents of a Java InputStream to an OutputStream
According to the documentation of the FTPClient.retrieveFileStream() method,
You must close the InputStream when you finish reading from it. The InputStream itself will take care of closing the parent data connection socket upon being closed.
When you close the stream, your client connection will be closed too. So instead of using the same client over and over, you need to create a new client connection for each file.
I didn't see the output stream is not properly closed.
finalBytes is o bytes?
where you defined the buffer variable?
please log the path so that we can see the path is correct or not. I guess the stream which is not properly closed makes the issue

Files shared through java socket programming end up being corrupt at the receiving end

I am trying to create a client/server program that allows a server and client to send files to each other. I created the sockets, and connected the client to the server. I am doing this one the same computer for now. if it is perfected, i will take it to another computer and try it.
My problem is that the file is transferred successfully but it is corrupt. the file received is corrupt, but the original is okay. I've had problems with socket exception where the socket keeps resetting after sending the file, but I've managed to solve that problem. Now the file is sent, but it is not complete.
The size of the file received is smaller than the size of the file sent, and this causes the received file not work. I sent a pdf file over the network. the original was about 695kb, but the received file was 688kb, and this caused the document to be corrupt. I also tried sending a video, and had the same result. the received file is smaller than the sent file.
I have checked the program, but I can't see where the problem is coming from.
The sending method i try to implement is the zero-copy method, where the data from the file is sent directly to the socket, from where it is read directly to the file. i did not use the other method where it is stored in a buffer before it is sent to the output stream. This is because I want to be able to use the program to send large files. Large files will fill up the java heap memory, and besides this method is faster.
buffer method:
....
File file = new File("path to file);
BufferedInputStream = new BufferedInputStream(new FileInputStream(file));
BufferedOutputStream out = new BufferedOutputStream(socket.getOutputStream());
byte[] buf = new byte[length];
in.read(buf, 0, buf.length);
out.write(buf, 0, buf.length);
....
I did not use this buffer method. Here is my code. This is the file server
import java.io.*;
import java.net.*;
import javax.swing.JFileChooser;
public class ShareServer {
public static void main(String args[]) {
int port = 4991;
ServerSocket server;
Socket socket = null;
BufferedInputStream in = null;
BufferedOutputStream out = null;
try {
server = new ServerSocket(port);
System.out.println("Waiting for connection request..");
socket = server.accept();
System.out.println("Connected to " + socket.getInetAddress().getHostName());
JFileChooser fc = new JFileChooser();
File file = null;
if (fc.showOpenDialog(null) == JFileChooser.APPROVE_OPTION)
file = fc.getSelectedFile();
// send out the reference of the file using writeObject() method
new ObjectOutputStream(socket.getOutputStream()).writeObject(file);
in = new BufferedInputStream(new FileInputStream(file));
out = new BufferedOutputStream(socket.getOutputStream());
// send file
int b = 1;
while (b != -1){
b = in.read();
out.write(b);
}
System.out.println(file.getName() + " has been sent successfully!");
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
try {
in.close();
out.close();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Here is the Client class:
import java.io.*;
import java.net.*;
import javax.swing.JOptionPane;
public class ShareClient {
public ShareClient() {
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
String host = InetAddress.getLocalHost().getHostAddress();
Socket socket = new Socket(host, 4991);
System.out.println("Connected to " + host);
// receive the file object. this does not contain the file data
File refFile = (File) new ObjectInputStream(socket.getInputStream()).readObject();
System.out.println("File to receive " + refFile.getName());
// create a new file based on the refFile
File newFile = new File(System.getProperty("user.home") + "/desktop/ReceivedFiles", refFile.getName());
BufferedInputStream in = new BufferedInputStream(socket.getInputStream());
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(newFile));
System.out.println("Receiving file now...");
int b;
while ((b = in.read()) != -1)
out.write(b);
System.out.println("File has been received successfully!");
socket.close();
}
}
The server and the client classes run successfully without any exceptions, and the file is sent, but it is corrupt. it is incomplete.
Take note that the file sent through the ObjectInput and ObjectOutput streams is not the real file, but just a file object that has all the information of the filie i want to send, but not the binary data of the file.
Please can anybody help me? Why is the file corrupt or incomplete? It is read to the end (when -1) is returned, and all the bytes are sent, but for some reason i can't explain, it ends up being less than the size of the original file.
Currently, you write -1 at the end of the file (that's when you should stop). Something like,
int b = 1;
while (b != -1){
b = in.read();
if (b != -1) {
out.write(b);
}
}
or
int b;
while ((b = in.read()) != -1) {
out.write(b);
}
I have finally got the answer to the problem! It was something so simple! the buffer. I just added a small line of code to flush the socket outputstream buffer in the server, and flush the fileoutputstream buffer in the client program, and that was it! It seems some bytes of data was left in the buffer and that was making the file to be incomplete. this is one of the problems of buffered input and output. if you forget to flush the buffer, you start running into problems.
here's the code:
int b = 1;
while(b != -1){
out.write(b);
}
out.flush(); //this solved my problem. I also did it in the client class
Thank you so much for your answer #Elliot Frisch :)

Java Socket read and write unexpected behavior

I faced an issue while transferring file from client to server. So I did a simple client and server code in java using sockets. I found some unexpected behavior. I can't find why it happens so. Following is the code
Server side code:
import java.io.*;
import java.net.*;
class Server
{
public static void main(String args[])
{
ServerSocket ser;
try {
ser=new ServerSocket(9000);
Socket cnfcon=ser.accept();
OutputStream outstr=cnfcon.getOutputStream();
InputStream inpstr=cnfcon.getInputStream();
PrintWriter out=new PrintWriter(outstr,true);
BufferedReader inp=new BufferedReader(new InputStreamReader(inpstr));
File f=new File("test.txt");
InputStream fis= new FileInputStream(f);
long size=f.length();
System.out.println("Start Server");
out.println(f.getName());
out.println(size);
byte[] buff1=new byte[]{0,1,2,3,4,5,6,7,8,9};
byte[] buff2=new byte[]{7,1,2,3,8,5,6,7,8,9};
outstr.write(buff1);
//inpstr.read(); -- tried including this and removing this
outstr.write(buff2);
//inpstr.read();
fis.close();
inp.close();
ser.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
client side code:
import java.io.*;
import java.net.*;
class test
{
public static void main(String args[]) throws UnknownHostException, IOException
{
Socket cnfcon=new Socket("127.0.0.1",9000);
OutputStream outstr=cnfcon.getOutputStream();
InputStream inpstr=cnfcon.getInputStream();
PrintWriter out=new PrintWriter(outstr,true);
BufferedReader inp=new BufferedReader(new InputStreamReader(inpstr));
File f=new File(inp.readLine()+"t"); //"t" - dummy
f.createNewFile();
FileOutputStream fos=new FileOutputStream(f);
int size=Integer.parseInt(inp.readLine());
byte buff[]=new byte[1024];
System.out.println("Start Client");
inpstr.read(buff, 0, 10);
disp(buff);
//outstr.write(1); -- tried including this and removing this
inpstr.read(buff, 0, 10);
disp(buff);
//outstr.write(1); 1 - dummy value
fos.close();
out.close();
cnfcon.close();
}
public static void disp(byte buff[])
{
for(int i=0;i<10;i++)
System.out.print(buff[i]);
System.out.println();
}
};
I am sending two buffers from server to client.
Here I expect first buffer should come first and next for next time.
But its unpredictable.
Some times it works as expected. Some times it gets the 2nd buff for both time. Some times all bytes in the buffer are zero.
I tried adding outstr.flush() before the message passing.
Is there any other way to implement this?
To be more clear, it seems client is not waiting for server to send or vice versa.
The output is different every time.
Suggestions are appreciated.
The problems are at least two:
You're ignoring the count returned by read(), and assuming it filled the buffer. It isn't obliged to do that. It isn't contracted to transfer more than one byte. You have to loop:
while ((count = in.read(buffer)) > 0)
{
out.write(buffer, 0, count);
}
Data is disappearing into the buffered readers/writers/streams, and it is caused by using too many parts of the I/O stack. Don't use multiple readers, writers, and streams, on the same socket. Use a single output stream or writer and a single input stream or reader for the life of the socket. As you are writing binary data you shouldn't be using readers or writers at all.
Contrary to other answers here, it isn't necessary to add flushes or sleeps in this code, as long as you close everything correctly, or to fiddle around with available() results either.
Try adding a Thread.sleep() in the client when it receives the buffers? Not sure if it will work.
Let me guess , you are on Microsoft windows ?
First chunk your data into small chunks ( let's say 1kb ), but you are already doing that and then sleep a little bit in both your read and write loops. Call flush when possible after writes, and Be sure to make sure you don't read more than input stream.availible()
Your code should work on every other os without issue , without modification.

Java request file, send file (Client-server)

I'm making a Client-Server. I've gotten as far as that the server can send a hardcoded file, but not a client specified. I will have to send only text files. As far as I have understood: the clients firstly sends the file name and then, the server sends it, nothing complicated, but I'm getting all kinds of errors, this code is getting a connection reset/socket closed error. The main problem is, that hadn't got much time to research networking.
Ill appreciate any help I can get.
EDIT.
I found a work around, closing a stream causes the socket to close, why is that? It shouldn't happen, should it?
Server Side:
InputStream sin=newCon.getInputStream();
DataInputStream sdata=new DataInputStream(sin);
location=sdata.readUTF();
//sdata.close();
//sin.close();
File toSend=new File(location);
byte[] array=new byte[(int)toSend.length()];
FileInputStream fromFile=new FileInputStream(toSend);
BufferedInputStream toBuffer=new BufferedInputStream(fromFile);
toBuffer.read(array,0,array.length);
OutputStream out=newCon.getOutputStream(); //Socket-closed...
out.write(array,0,array.length);
out.flush();
toBuffer.close();
newCon.close();
ClientSide:
int bytesRead;
server=new Socket(host,port);
OutputStream sout=server.getOutputStream();
DataOutputStream sdata=new DataOutputStream(sout);
sdata.writeUTF(interestFile);
//sdata.close();
//sout.close();
InputStream in=server.getInputStream(); //socket closed..
OutputStream out=new FileOutputStream("data.txt");
byte[] buffer=new byte[1024];
while((bytesRead=in.read(buffer))!=-1)
{
out.write(buffer,0,bytesRead);
}
out.close();
server.close();
Try reading the file in chunks from Server while writing to client output stream rather than creating a temp byte array and reading entire file into memory. What if requested file is large? Also close the new Socket on server-side in a finally block so socket is closed even if an exception is thrown.
Server Side:
Socket newCon = ss.accept();
FileInputStream is = null;
OutputStream out = null;
try {
InputStream sin = newCon.getInputStream();
DataInputStream sdata = new DataInputStream(sin);
String location = sdata.readUTF();
System.out.println("location=" + location);
File toSend = new File(location);
// TODO: validate file is safe to access here
if (!toSend.exists()) {
System.out.println("File does not exist");
return;
}
is = new FileInputStream(toSend);
out = newCon.getOutputStream();
int bytesRead;
byte[] buffer = new byte[4096];
while ((bytesRead = is.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
out.flush();
} finally {
if (out != null)
try {
out.close();
} catch(IOException e) {
}
if (is != null)
try {
is.close();
} catch(IOException e) {
}
newCon.close();
}
If you use Apache Common IOUtils library then you can reduce much of the code to read/write files to streams. Here 5-lines down to one line.
org.apache.commons.io.IOUtils.copy(is, out);
Note that having a server that serves files by absolute path to remote clients is potentially dangerous and the target file should be restricted to a given directory and/or set of file types. Don't want to serve out system-level files to unauthenticated clients.

Socket-transferred file: contents are empty

I am working on transferring a file between two computers over a socket. Everything seems to work, but when I look at the contents of the retrieved file, it is empty. What am I doing wrong?
Here is my server-side code. The file foobar.txt exists, and its contents are "hello world!".
try{
ServerSocket ssock = new ServerSocket(12345);
Socket sock = ssock.accept();
//here I get the filename from the client, but that works fine.
File myFile = new File("foobar.txt");
byte[] mybytearray = new byte[(int) myFile.length()];
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(myFile));
bis.read(mybytearray, 0, mybytearray.length);
OutputStream os = sock.getOutputStream();
os.write(mybytearray, 0, mybytearray.length);
os.flush();
sock.close();
} catch (Exception e){
e.printStackTrace();
}
And here is my client code:
try {
Socket socket = new Socket(host, port);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
out.print("get foobar.txt\r\n");
out.flush();
byte[] streamIn = new byte[1024];
InputStream in = socket.getInputStream();
FileOutputStream file_src = new FileOutputStream("foobar.txt");
BufferedOutputStream file_writer = new BufferedOutputStream(file_src);
int i;
while ((i = in.read()) != -1) {
file_writer.write(i);
}
file_writer.flush();
file_writer.close();
file_src.close();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
Solved
Since I am using multiple threads and multiple sockets and testing all connections on one machine, I was simply running into a problem where the client (which has both the client and server code in it) would connect with itself instead of the other client. Changing the file transfer port for the different running clients got this all to work. Thanks for everyone who had a look at this and gave me some suggestions.
Maybe you're closing the wrong socket on the client. When you close the socket, you're closing the class field this.socket instead of the local variable socket.
Also, when you close the output stream to the file, you don't have to close both the BufferedOutputStream and the FileOutputStream. The FileOutputStream is automatically closed when the BufferedOutputStream is closed.
One more thing---you don't have to flush an output stream before closing it. When you call close() the stream is automatically flushed.
In addition to what everyone else has said, you are ignoring the result of bis.read(). It isn't guaranteed to fill the buffer. See the Javadoc.
The correct way to copy streams in Java, which you should use at both ends, is this:
byte[] buffer = new byte[8192]; // or whatever
int count;
while ((count = in.read(buffer)) > 0)
{
out.write(buffer, 0, count);
}
The only thing I think of that is that you actually never start receiving the file because the server-side doesn't read the command ("get foobar.txt"), so the client-side freezes on sending the command.
The existence of the file at the client-side might be from previous tests.
But, I'm not sure this is the problem. It's just a try to help.

Categories

Resources