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.
Related
I am trying to send pdf files over to clients using tcp connections. Somehow, the file is corrupted. I wonder if tcp support non-txt files?
this is a simple server program
import java.io.*;
import java.net.*;
class server{
public static void main (String args[]) throws Exception
{
Socket s = null;
int firsttime=1;
while(true) {
ServerSocket s_sock = new ServerSocket(3248);
Socket socket = s_sock.accept();
BufferedReader bf = new BufferedReader(new InputStreamReader(socket.getInputStream()));
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
File myFile = new File ("/Users/wchen48/Desktop/123.pdf");
byte [] mybyte = new byte[(int)myFile.length()];
OutputStream os = socket.getOutputStream();
System.out.println("sending......" + mybyte.length + "Byte");
os.write(mybyte,0,mybyte.length);
os.flush();
socket.close();
s_sock.close();
}
} //end of main
This is the client:
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.Socket;
public class client{
public static void main(String[] argv) throws Exception {
Socket sock = new Socket("127.0.0.1", 3248);
byte[] mybytearray = new byte[11536];
InputStream is = sock.getInputStream();
FileOutputStream fos = new
FileOutputStream("/Users/wchen48/Documents/123.pdf");
bos = new BufferedOutputStream(fos);
int bytesRead = is.read(mybytearray, 0, mybytearray.length);
bos.write(mybytearray, 0, mybytearray.length);
bos.close();
sock.close();
}
}
Thank you in advance.
What Zielu and Juned said, plus:
You need loops at both server and client.
At the server, choose a reasonable-size buffer and loop around. Try to read [buffer length] bytes into it from the file. Then USE THE RETURNED VALUE FROM THE READ CALL to both check for errors, (result<0), or end-of-file, (result 0), or use the value in the network write call as the number of bytes to write. DO NOT use the buffer length - it may not be full. When you get 0, close the file and socket, else loop back and read some more.
At the client, choose a reasonable-size buffer and loop around. Try to read [buffer length] bytes into it from the socket. Then USE THE RETURNED VALUE FROM THE READ CALL to both check for errors, (result<0), or socket close, (result 0), or use the value in the file write call as the number of bytes to write. DO NOT use the buffer length - it may not be full. When you get 0, exit the loop and close the file, else loop back and read some more.
you dont read content of your file into the buffer mybyte you later send through socket, so you send 00000....
The most likely reason for getting the corrupted file is the fixed number of bytes(11536) that you are trying to read. You should try to read all the bytes received on the socket rather than fixed number of bytes.
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!
Ok, So I'm making a Java program that has a server and client and I'm sending a Zip file from server to client. I have sending the file down, almost. But recieving I've found some inconsistency. My code isn't always getting the full archive. I'm guessing it's terminating before the BufferedReader has the full thing. Here's the code for the client:
public void run(String[] args) {
try {
clientSocket = new Socket("jacob-custom-pc", 4444);
out = new PrintWriter(clientSocket.getOutputStream(), true);
in = new BufferedInputStream(clientSocket.getInputStream());
BufferedReader inRead = new BufferedReader(new InputStreamReader(in));
int size = 0;
while(true) {
if(in.available() > 0) {
byte[] array = new byte[in.available()];
in.read(array);
System.out.println(array.length);
System.out.println("recieved file!");
FileOutputStream fileOut = new FileOutputStream("out.zip");
fileOut.write(array);
fileOut.close();
break;
}
}
}
} catch(IOException e) {
e.printStackTrace();
System.exit(-1);
}
}
So how can I be sure the full archive is there before it writes the file?
On the sending side write the file size before you start writing the file. On the reading side Read the file size so you know how many bytes to expect. Then call read until you have gotten everything you expect. With network sockets it may take more than one call to read to get everything that was sent. This is especially true as your data gets larger.
HTTP sends a content-length: x+\n in bytes. This is elegant, it might throw a TimeoutException if the conn is broken.
You are using a TCP socket. The ZIP file is probably larger than the network MTU, so it will be split up into multiple packets and reassembled at the other side. Still, something like this might happen:
client connects
server starts sending. The ZIP file is bigger than the MTU and therefore split up into multiple packets.
client busy-waits in the while (true) until it gets the first packets.
client notices that data has arrived (in.available() > 0)
client reads all available data, writes it to the file and exits
the last packets arrive
So as you can see: Unless the client machine is crazily slow and the network is crazily fast and has a huge MTU, your code simply won't receive the entire file by design. That's how you built it.
A different approach: Prefix the data with the length.
Socket clientSocket = new Socket("jacob-custom-pc", 4444);
DataInputStream dataReader = new DataInputStream(clientSocket.getInputStream());
FileOutputStream out = new FileOutputStream("out.zip");
long size = dataReader.readLong();
long chunks = size / 1024;
int lastChunk = (int)(size - (chunks * 1024));
byte[] buf = new byte[1024];
for (long i = 0; i < chunks; i++) {
dataReader.read(buf);
out.write(buf);
}
dataReader.read(buf, 0, lastChunk);
out.write(buf, 0, lastChunk);
And the server uses DataOutputStream to send the size of the file before the actual file. I didn't test this, but it should work.
How can I make sure I received whole file through socket stream?
By fixing your code. You are using InputStream.available() as a test for end of stream. That's not what it's for. Change your copy loop to this, which is also a whole lot simpler:
while ((count = in.read(buffer)) > 0)
{
out.write(buffer, 0, count);
}
Use with any buffer size greater than zero, typically 8192.
In.available() just tells you that there is no data to be consumed by in.read() without blocking (waiting) at the moment but it does not mean the end of stream. But, they may arrive into your PC at any time, with TCP/IP packet. Normally, you never use in.available(). In.read() suffices everything for the reading the stream entirely. The pattern for reading the input streams is
byte[] buf;
int size;
while ((size = in.read(buf)) != -1)
process(buf, size);
// end of stream has reached
This way you will read the stream entirely, until its end.
update If you want to read multiple files, then chunk you stream into "packets" and prefix every one with an integer size. You then read until size bytes is received instead of in.read = -1.
update2 Anyway, never use in.available for demarking between the chunks of data. If you do that, you imply that there is a time delay between incoming data pieces. You can do this only in the real-time systems. But Windows, Java and TCP/IP are all these layers incompatible with real-time.
I am getting below EOFException while using readUTF() method, please let me know how could i overcome with this problem and also please suggest how readUTF() transfers socket information over the other networks
import java.io.*;
import java.net.*;
public class GreetingServer {
public static void main(String args[]) {
String servername =args[0];
int port = Integer.parseInt(args[1]);
try {
System.out.println("Server Name "+ servername +"Port"+port);
Socket client = new Socket(servername,port);
System.out.println("Just connected to"+ client.getRemoteSocketAddress());
OutputStream outs = client.getOutputStream();
DataOutputStream dout = new DataOutputStream(outs);
dout.writeUTF("Hello From"+client.getRemoteSocketAddress());
InputStream in = client.getInputStream();
DataInputStream din = new DataInputStream(in);
System.out.println("Server Says"+ din.readUTF());
client.close();
}
catch (EOFException f) {
f.printStackTrace();
}
catch(IOException e) {
e.printStackTrace();
}
}
}
You have reached the end of the stream. There is no more data to read.
Possibly your server isn't using writeUTF(), or you are out of sync with it. If the server is writing lines you should be using BufferedReader.readLine().
The docs for readUtf() state;
First, two bytes are read and used to construct an unsigned 16-bit
integer in exactly the manner of the readUnsignedShort method . This
integer value is called the UTF length and specifies the number of
additional bytes to be read. These bytes are then converted to
characters by considering them in groups. The length of each group is
computed from the value of the first byte of the group. The byte
following a group, if any, is the first byte of the next group.
This suggests to me that what your trying to read with readUtf() isn't UTF, as the EOFException occurs when the End of File (EOF) is read unexpectedly.
Check that you are reading the right types in the same order that your server is sending them etc. You should be following a decided protocol, rather than blindly reading.
I have a server written in VB, it sends data to clients in periods of 3 seconds. I've written a client Java code like this:
class Commu extends Thread {
Socket socket;
InputStream inputStream;
OutputStream outputStream;
public Commu() {
try {
socket=new Socket();
socket.connect(new InetSocketAddress("192.168.0.1", 1234)));
inputStream=socket.getInputStream();
outputStream=socket.getOutputStream();
this.start();
} catch(Exception e) {
System.out.println(e);
}
}
public void run() {
while(true) {
byte[] buffer=new byte[1024];
inputStream.read(buffer);
System.out.println(buffer[0]);
}
}
}
It works fine on my desktop, it prints message whenever the VB server sends message.
It works on Android, but the inputStream read only once, and then get stuck; If I want to read more data, I have to use outputStream to send some data, then inputStream will read once, and get stuck again. That is really strange, could anyone tell me why?
There is no problem in System.out.print(), because DDMS can show it, I promise! The problem is inputStream should not read only once, it should read when data comes. But it didn't , it read only once.
Even if, I only print one byte from the buffer, it get stuck on Android. It works very well on desktop, but get stuck on Android.
That's a wierd piece of code. You don't check the return value of read() for -1, i.e. EOS, and you only display the first byte of the data received and throw the rest away.