I have some client server socket code and am transferring binary files between a Windows 7 machine and a SUSE Linux box. When I transfer a file from Win7 to Linux, they end up with the same MD5 Checksum so I know they are identical. But when I transfer from Linux to Win7, the checksums don't agree, indicating that the file did not transfer properly.
Anybody run into this? I'm using ObjectOutputStreams and DataInputStreams and the code is the same on both sides.
// connect socket to server socket, etc
//........
//=======================
// read the file
try {
size = file.length();
byteArr = new byte[(int) size];
dis = new DataInputStream(new FileInputStream(file));
dis.read(byteArr, 0, byteArr.length);
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
//=======================
// then send it
try {
oos.writeObject(byteArr);
oos.flush();
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
// then close oos, dis, etc
InputStream.read is not guaranteed to fill the input array. It guarantees at least 1 byte is read and no more than can fit in the array, or 0 bytes if the end of file is reached, or an IOException. It may be that under linux you are not getting the whole file in one go.
Also, why are you filling a byte array and then sending it as an object? Streams are conceptualised as streams of bytes, there is no need for an array.
eg.
int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];
FileInputStream input = new FileInputStream(file);
int read;
while ((read = input.read(buffer)) != -1) {
output.write(buffer, 0, read);
}
// flush and close everything
Related
I built an application in java on android that sends the bytes of a .jpg file through a socket and an Asynchronous Server Socket in C# on windows that receives the bytes and writes them into a .jpg a file. The received file can't be open.
In the java application, which is the client, I used the folowing asynctask class executed on the onCreate:
class asyncTask extends AsyncTask<Void,Void,Void>{
#Override
protected Void doInBackground(Void... voids) {
try {
final File myFile = new File("//sdcard/DCIM/Camera/img1.jpg");
byte[] bytes = new byte[(int) myFile.length()];
BufferedInputStream buf = new BufferedInputStream(new FileInputStream(myFile));
buf.read(bytes, 0, bytes.length);
s = new Socket(ip,3333);
OutputStream OS = s.getOutputStream();
OS.write(bytes, 0, bytes.length);
OS.flush();
s.close();
} catch (IOException e) {
Log.d("Error",e.toString());
e.printStackTrace();
}
return null;
}
}
And in the C# application I received the data and saved it to a file in the following async callback method that is called when data is received:
public static void ReadCallback(IAsyncResult ar)
{
Socket clientSocket = (Socket)ar.AsyncState;
int buffLength = clientSocket.EndReceive(ar);
Array.Resize(ref buffer, buffLength);
totaLength += buffLength;
buffer.CopyTo(allData, totaLength - 1);
if (buffLength == 0)
{
Array.Resize(ref allData, totaLength);
File.WriteAllBytes("C: /Users/fxavi/Desktop/img.jpg", allData);
Console.WriteLine(totaLength);
}
else
clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReadCallback), clientSocket);
}
buf.read(bytes, 0, bytes.length);
That will mostly not read the requested amount of bytes.length bytes.
Change that first to
int nread = buf.read(bytes, 0, bytes.length);
And compare nread to bytes.length and discover that they are not equal.
Further it is a bad idea to load a file completely in memory. Imagine you want to send a GB file.
Better make a loop where you read a chunck from the input stream and then write that chunck to the output stream.
I feel really stupid after finding the solution for this problem. Which is that totaLength += buffLength; should be executed before buffer.CopyTo(allData, totaLength); so that the buffer is copied to the right index.
I am trying to read an image and deliver it through a Java socket. But there are some bits that does not fit. When viewing in a diff tool I realized that all numbers bigger than 127 were truncated.
So I wanted to just convert it to a char[] array and return it instead. Now I'm getting a complette different image, perhaps due to char's size.
try (PrintWriter out = new PrintWriter(this.socket.getOutputStream(), true);
BufferedInputStream in = new BufferedInputStream(new FileInputStream(filename), BUFSIZ)) {
byte[] buffer = new byte[BUFSIZ];
while (in.read(buffer) != -1) {
response.append(new String(buffer));
out.print(response.toString());
response.setLength(0);
}
} catch (IOException e) {
System.err.println(e.getMessage());
}
This is my reading and delivering code.
I've read many times to use ImageIO but I want to do it without, since I don't know whether it's an image or not. (And what about other file types like executables?)
So, is there any way to convert it to something like an unsigned byte that'll be delivered correctly on the client? Do I have to use something different than read() to achieve that?
Writers are for character data. Use the OutputStream. And you're making the usual mistake of assuming that read() filled the buffer.
The following loop will copy anything correctly. Memorize it.
int count;
byte[] buffer = new byte[8192];
while ((count = in.read(buffer)) > 0)
{
out.write(buffer, 0, count);
}
Repeat after me: a char is not a byte and it's not a code point.
Repeat after me: a Writer is not an OutputStream.
try (OutputStream out = this.socket.getOutputStream();
BufferedInputStream in = new BufferedInputStream(new FileInputStream(filename), BUFSIZ)) {
byte[] buffer = new byte[BUFSIZ];
int len;
while ((len = in.read(buffer))) != -1) {
out.write(buffer, 0, len);
}
} catch (IOException e) {
System.err.println(e.getMessage());
}
(this is from memory, check the args for write()).
I need a very simple function that allows me to read the first 1k bytes of a file through FTP. I want to use it in MATLAB to read the first lines and, according to some parameters, to download only files I really need eventually. I found some examples online that unfortunately do not work. Here I'm proposing the sample code where I'm trying to download one single file (I'm using the Apache libraries).
FTPClient client = new FTPClient();
FileOutputStream fos = null;
try {
client.connect("data.site.org");
// filename to be downloaded.
String filename = "filename.Z";
fos = new FileOutputStream(filename);
// Download file from FTP server
InputStream stream = client.retrieveFileStream("/pub/obs/2008/021/ab120210.08d.Z");
byte[] b = new byte[1024];
stream.read(b);
fos.write(b);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fos != null) {
fos.close();
}
client.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
the error is in stream which is returned empty. I know I'm passing the folder name in a wrong way, but I cannot understand how I have to do. I've tried in many way.
I've also tried with the URL's Java classes as:
URL url;
url = new URL("ftp://data.site.org/pub/obs/2008/021/ab120210.08d.Z");
URLConnection con = url.openConnection();
BufferedInputStream in =
new BufferedInputStream(con.getInputStream());
FileOutputStream out =
new FileOutputStream("C:\\filename.Z");
int i;
byte[] bytesIn = new byte[1024];
if ((i = in.read(bytesIn)) >= 0) {
out.write(bytesIn);
}
out.close();
in.close();
but it is giving an error when I'm closing the InputStream in!
I'm definitely stuck. Some comments about would be very useful!
Try this test
InputStream is = new URL("ftp://test:test#ftp.secureftp-test.com/bookstore.xml").openStream();
byte[] a = new byte[1000];
int n = is.read(a);
is.close();
System.out.println(new String(a, 0, n));
it definitely works
From my experience when you read bytes from a stream acquired from ftpClient.retrieveFileStream, for the first run it is not guarantied that you get your byte buffer filled up. However, either you should read the return value of stream.read(b); surrounded with a cycle based on it or use an advanced library to fill up the 1024 length byte[] buffer:
InputStream stream = null;
try {
// Download file from FTP server
stream = client.retrieveFileStream("/pub/obs/2008/021/ab120210.08d.Z");
byte[] b = new byte[1024];
IOUtils.read(stream, b); // will call periodically stream.read() until it fills up your buffer or reaches end-of-file
fos.write(b);
} catch (IOException e) {
e.printStackTrace();
} finally {
IOUtils.closeQuietly(inputStream);
}
I cannot understand why it doesn't work. I found this link where they used the Apache library to read 4096 bytes each time. I read the first 1024 bytes and it works eventually, the only thing is that if completePendingCommand() is used, the program is held for ever. Thus I've removed it and everything works fine.
I have a case where the server sends the file size first and the file data. How do I differentiate both the integer value and the file data when being read at the client side?
Sameple code for server (os is the bufferedoutputstream):
// Construct a 1K buffer to hold bytes on their way to the socket.
byte[] mybytearray = new byte[(int) myFile.length()];
//File Size
os.write((int) myFile.length());
FileInputStream fis = null;
System.out.println("test+sendbytes");
// Copy requested file into the socket's output stream.
try {
fis = new FileInputStream(myFile);
} catch (FileNotFoundException ex) {
// Do exception handling
}
BufferedInputStream bis = new BufferedInputStream(fis);
try {
bis.read(mybytearray, 0, mybytearray.length);
os.write(mybytearray, 0, mybytearray.length);
os.flush();
os.close();
os.close();
// File sent, exit the main method
return;
} catch (IOException ex) {
// Do exception handling
}
You need to write the length as an int unless you are assuming all files arfe no more than 255 bytes long. Try DataOutputStream.writeInt()
For the read you have to assume an order. ie you assume the length is sent first followed by the contents. Use DataInputStream.readInt() to read the length.
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.