Java - Read file from Socket (while loop never ends) - java

My JAVA application sends a command to server (command=filename.ini). When the server receives this command it sends filename.ini contents through Socket.
The first problem I had was receiving only partial contents of the file. That happened when in the code I used while(in.available()!=0){//write bytes} because in.available() does not know how big/long the content of the file is. If I use while((numBytesRead = dis.read(buffer)) != -1){//write bytes} the loop will never terminate since the Socket connection remains always open. My question is how else can I terminate the loop once every byte has been received? Please help me I have tried everything. I understand where the mistake is but I don't know how to fix it.
The following is the part of the code I have at the moment:
public class TCPClient {
protected Socket s = null;
public DataInputStream in = null;
public TCPClient(InetAddress ipa, int port) {
Socket s1 = null;
try { //Open the socket.
s1 = new Socket(ipa.getHostAddress(), port);
} catch (IOException ex) {
System.out.println("Error opening socket!");
return;
}
s = s1;
try { //Create an input stream.
in = new DataInputStream(new BufferedInputStream(s.getInputStream()));
} catch (Exception ex) {
System.out.println("Error creating input stream!");
}
}
public synchronized byte[] receive() {
byte[] buffer = new byte[0];
ByteArrayOutputStream getBytes = new ByteArrayOutputStream();
try {
while (in.available() == 0) {
} //Wait for data.
} catch (IOException ex) {
}
try {
int numBytesRead;
buffer = new byte[1024];
while ((numBytesRead = dis.read(buffer, 0, 1024)) != -1) { //LOOP NEVER ENDS HERE BECAUSE CONNECTION IS ALWAYS OPEN
getBytes.write(buffer, 0, numBytesRead);
}
} catch (IOException ex) {
}
return (getBytes.toByteArray());
}
}

You need to define a micro protocol to say the receiver how long is the file, or just close the connection on the server after finishing sending the file. First method is preferred, since it is a little bit more robust. On the client you should have a timeout too in order to avoid to wait forever in case of network problems.
Clarification for micro protocol: before sending the file itself send a 32 (or 64 if needed) bit integer containing the file length. The client should read that integer and then start retrieving the file.

Related

Stream isn't closing after sending file

Excuse me my understanding of streams is slowly building.
I initally tried to stream a music file over to my client but that didn't work out so I moved transferring the entire file via bytes then saving it at the client side. The problem is the input stream is still receiving bytes and so won't break out of the while loop (Where I want to play the song)
Below is part of my client code:
//This is part of a task that carries this out (Part of a UI application)
#Override
protected Void call()
{
try(Socket socket = new Socket(host,portNumber);
ObjectOutputStream toServer = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream fromServer = new ObjectInputStream(socket.getInputStream()))
{
//Client requests a song from the server (song is a String)
toServer.writeUTF(".Music."+song);
toServer.flush();
//Create a new file (tempSong is a string)
File test = new File(tempSong);
test.createNewFile();
//New file writer
BufferedOutputStream bOS = new BufferedOutputStream(new FileOutputStream(test));
byte[] buffer = new byte[4096];
int current;
/**
* Read the bytes from the server and write the file
* The file is written and I can play it (externally)
* but the while loop doesn't break after writting the file
*/
while ((current = fromServer.read(buffer)) > 0)
{
bOS.write(buffer, 0 , current);
}
System.out.println("Finished writing");
bOS.close();
/**
* down here a method is ran to play the file
* but it never happen because the task is still in the while loop
*/
}
catch (IOException e)
{
e.printStackTrace();
}
return null;
}
below is in the server where the server reads the message and sends the file
/** This is part of a task and the main call area */
#Override
public Void call ()
{
try
{
//Setup I/O
toClient = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream fromClient = new ObjectInputStream(socket.getInputStream());
while(!socket.isClosed())
{
//If server has received a message
if(fromClient.available() > 0)
{
//Reads message and objects from client
String input = fromClient.readUTF();
if (input.contains(".Music"))
{
findMusic(input, toClient);
}
/**
* more else IFs
*/
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
/**
* this method is part of the task discussed above
*/
//The method that is called
private void findMusic(String input, ObjectOutputStream toClient)
{
logoff();
String[] names = input.split("[.]");
clientManagerTemp.logger("Someone request song: " + names[2] + ".mp3");
File musicFile = AudioUtil.getSoundFile("src/Resources/Songs/" + names[2]+ ".mp3");
byte[] buffer = new byte[(int) musicFile.length()];
try(BufferedInputStream bis = new BufferedInputStream(new FileInputStream(musicFile)))
{
bis.read(buffer, 0, buffer.length);
clientManagerTemp.logger("Sending " + "src/Resources/Songs/" + names[2]+ ".mp3" + "(" + buffer.length + " bytes)");
//write the file to the client
toClient.write(buffer,0, buffer.length);
toClient.flush();
clientManagerTemp.logger("Finished sending");
}
catch (IOException e)
{
e.printStackTrace();
}
}
So as you can see the server sends the file fine and my client receives it. It just won't stop the while loop. Can someone explain why? So I can get a better understanding of how streaming bytes over sockets work
**EDIT
When the client receives the file it can be played even after closing the client and server
You can send the file-length to the client, with this information the client knows when to exit the loop.
Server
private void findMusic(String input, ObjectOutputStream toClient)
{
logoff();
String[] names = input.split("[.]");
clientManagerTemp.logger("Someone request song: " + names[2] + ".mp3");
File musicFile = AudioUtil.getSoundFile("src/Resources/Songs/" + names[2]+ ".mp3");
int fileLength = (int) musicFile.length();
byte[] buffer = new byte[fileLength];
try(BufferedInputStream bis = new BufferedInputStream(new FileInputStream(musicFile)))
{
bis.read(buffer, 0, buffer.length);
clientManagerTemp.logger("Sending " + "src/Resources/Songs/" + names[2]+ ".mp3" + "(" + buffer.length + " bytes)");
//write the file to the client
toClient.writeInt(fileLength);
toClient.write(buffer,0, buffer.length);
toClient.flush();
clientManagerTemp.logger("Finished sending");
}
catch (IOException e)
{
e.printStackTrace();
}
}
Client
byte[] buffer = new byte[4096];
int current;
int fileLength = fromServer.readInt();
while ( fileLength > 0 && (current = fromServer.read(buffer, 0, Math.min(4096,fileLength))) > 0)
{
bOS.write(buffer, 0 , current);
fileLength -= current;
}
This is due to the nature of the read method, seen here Javadocs. This method will block until there is data, so your loop will never end. Now the reason for this is because you never close the stream on the server end, you just flush it, which forces a send of all the data currently in the buffer, but will not close the stream. If you call .close() on the stream from the server side then that should exit the while loop on the client side and continue to your playing code.
I haven't tested this, but from the documentation and a brief look at your code, that seems to be the issue.
If you don't close the connection (server side), there will not be an end-of-file/end-of-stream and your client side will run the loop forever, unless you configured a read timeout.
If you won't to close the loop automatically after sending the file is finished, send the count of bytes (size of file) first to client and then the file itself. This makes it possible to read only a exact number of bytes on client side and close the connection when finished.
Alternative you can send a special sequence and check for them at client side to mark the end of stream.
The reason you program is stuck is due to the behaviour of ObjectInputStream.read(). This method is blocking until it has read some data or the stream is closed. Since you never close the stream the 2nd condition will never be met. So there is only option 1. To return when data arrived. Unfortuenatly there is no way to recognize the end of a file unless you define it yourself e.g. by sending a unique sequenz or something that you can recognize to break the while loop.
Example:
while ((current = fromServer.read(buffer)) > 0)
{
// example value 42 could be anything else as well
if(current == -42)
{
break;
}
bOS.write(buffer, 0 , current);
}
The downside of this is, that your data could also contain this special value so you gotta think about different possibilities e.g memorize the sequence up to 3 values and compare them to an 'interrupt sequence'.

Socket connectivity loss when sending live streaming data

I'm struggling here...
I'm trying to determine if data was successfully sent to the server through a TCP socket using the OutputStream object. For testing on emulator socket communications is loss after 30 sec. For write data OutputStream.write(); its doesn't throw an exception , and local server continuously running its not crashing, only tcp socket connection is loss after some time. All the methods in the socket class return as though the socket is active and working. Is there anything I'm doing wrong here? Is there any socket implementation or stream implementation I can use to get an exception or error when the stream/ socket doesn't actually send the data in the buffer? Also setting setSoTimeout() on the socket doesn't seem to do anything.
Please guide me...
Here is my code:
private void sendRec() {
int lstream;
int port = 1012;
byte[] byterecv = new byte[1040];
while (true) {
System.out.println("POOL-2");
synchronized (recSendThread) {
try {
recSendThread.wait(20);
} catch (InterruptedException e) {
}
}
if (stopcall == true) {
// break;
}
try {
// Provides a client-side TCP socket
Socket clientRec = new Socket();
// serverSocket = new ServerSocket(port);
// serverSocket.setSoTimeout(5000);
// Connects this socket to the given remote host address and
// port
clientRec.connect(new InetSocketAddress("192.168.1.36", port));
System.out.println("Just connected to "
+ clientRec.getRemoteSocketAddress());
System.out.println("SENTS Rec BEFORE");
// output streams that write data to the network
OutputStream outToServerRec = clientRec.getOutputStream();
DataOutputStream outStreamRec = new DataOutputStream(
outToServerRec);
outStreamRec.write(bData);
System.out.println("SENTS Rec AFTER");
// input streams that read data from network
InputStream inFromServerRec = clientRec.getInputStream();
// clientRec.setSoTimeout(5000);
DataInputStream inStreamRec = new DataInputStream(
inFromServerRec);
while ((lstream = inStreamRec.read(byterecv)) != -1) {
System.out.println("startrec bytearray -- "
+ byterecv.length);
bos1.write(byterecv, 0, lstream);
}
inStreamRec.close();// for closing dataouputstream
clientRec.close();// for closing socket connection
} catch (IOException e) {
e.printStackTrace();
}
}
}
Here is my receiver and player code..
/**
* start receiving the voice data from server
* */
protected void startplay() {
System.arraycopy(frndid, 0, playByteData, 0, 4);
System.arraycopy(userid, 0, playByteData, 4, 4);
ByteBuffer.wrap(sessionid).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer().
put(call_sessionid);
System.arraycopy(sessionid, 0, playByteData, 8, 4);
int lstream;
int port = 1014;
while (true) {
System.out.println("POOL-3");
try {
if (stopcall == true) {
System.out.println("BREAKEDDDD1111");
//break;
}
// Host name
// port++;
InetAddress addressPlay = InetAddress.getByName("192.168.1.36");
// Creates a new streaming socket connected to the target host
Socket clientPlay = new Socket(addressPlay, port);
System.out.println("Just connected to play : " +
clientPlay.getRemoteSocketAddress());
System.out.println("SENTS Play BEFORE");
// output streams that write data
OutputStream outToServer = clientPlay.getOutputStream();
DataOutputStream outStreamPlay = new DataOutputStream(outToServer);
outStreamPlay.write(playByteData);
System.out.println("SENTS Play after");
// input streams that read data
InputStream inFromServerPlay = clientPlay.getInputStream();
DataInputStream inStreamPlay = new DataInputStream(inFromServerPlay);
//clientPlay.setSoTimeout(5000);
while ((lstream = inStreamPlay.read(byteArray)) != -1) {
System.out.println("startplay() bytearray -- " +
byteArray.length);
bos.write(byteArray, 0, lstream);
}
inStreamPlay.close();
clientPlay.close();// for closing play socket connection
responseBuffer = bos.toByteArray();
System.out.println("BAOSSIZE " + bos.size());
bos.reset();
bos.flush();
bos.close();
playing = true;
System.out.println("res length -- " + responseBuffer.length);
rcvbb=ByteBuffer.wrap(responseBuffer).order(ByteOrder.LITTLE_ENDIAN).
asShortBuffer().get(playShortData);
playVoiceReceived();// plays received data
} catch (ParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* start playing received the voice data from server
* */
public void playVoiceReceived() {
System.out.println("POOL-4");
try {
if (at != null) {
if (at.getPlayState() != AudioTrack.PLAYSTATE_PLAYING) {
at.play();// starts playing
} else {
System.out.println("Play BEFORE WRITE");
// Writes the audio data to the audio hardware for playback.
at.write(playShortData, 0, BufferElements2Play);
System.out.println("Play AFTER WRITE");
at.flush();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
The socket has sent the data ... to the local socket send buffer. What happens after that is up to the local TCP stack, the network, the remote TCP stack, and the remote application. If you want to know whether the remote application got the data, it will have to send you a reply.
Operator write does not check whether data was delivered because otherwise it would have to wait for too long time. If network connection is actually down, TCP layer of operating system, will try to send data anyway, but will detect problems somewhat later, i.e. 1 minute later, because it will not receive acknowledgement messages from the opposite side. It will then try to resend data several times, see that problem persists and only then will report exception condition on the socket. To know that socket is in exception condition, you need to perform some operator on socket, i.e. another write attempt. Try doing write in a loop like this:
while (true)
{
outStreamRec.write (data);
Thread.sleep (1000L);
}
It should throw an error about 2 minutes after network will be down.
Note, that in opposite to write operation, operation connect is synchronous, so it actually waits for response from the opposite side, and if there is not respose, it will throw an exception.

Sending picture over a socket (Java PC - Android)

I'm new to Java so I need help please. I'm writing an application that will onClick send a String to Server and Server needs to return an image using socket. So my client side is Android and server side is PC - java.
I think that my server side is ok (because he prints out all the system.out.print commands) but my client side is not good. Please tell my where is my mistake? Thanks!
Here is code of my Server (PC) side (socket is delivered thru function parameter):
try {
dataInputStream = new DataInputStream(socket.getInputStream());
poruka = "" + dataInputStream.readUTF();
System.out.print(poruka);
int bytecount = 2048;
byte[] buf = new byte[bytecount];
OutputStream OUT = socket.getOutputStream();
BufferedOutputStream BuffOUT = new BufferedOutputStream(OUT, bytecount);
FileInputStream in = new FileInputStream("screenShot.jpg");
int i = 0;
while ((i = in.read(buf, 0, bytecount)) != -1) {
BuffOUT.write(buf, 0, i);
System.out.print("check" + buf[0]);
BuffOUT.flush();
}
in.close();
BuffOUT.close();
System.out.print("over");
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
socket.close();
dataInputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
and here is my Client (Android) side:
Socket socket = null;
DataOutputStream dataOutputStream = null;
try {
socket = new Socket(IPadresa, 8888);
dataOutputStream = new DataOutputStream(socket.getOutputStream());
slanje = "hy string";
dataOutputStream.writeUTF(slanje);
FileOutputStream outToFile = new FileOutputStream("slika.jpg");
int bytecount = 2048;
byte[] buf = new byte[bytecount];
InputStream IN = socket.getInputStream();
BufferedInputStream BuffIN = new BufferedInputStream(IN, bytecount)
int i = 0;
int filelength = 0;
while((i = BuffIN.read(buf, 0, bytecount)) != -1) {
filelength += i;
outToFile.write(buf, 0, i);
outToFile.flush();
}
IN.close();
BuffIN.close();
dataOutputStream.close();
socket.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
MORE INFORMATIONS:
In server side I can see String that is send from Client. And I have that System.out.print("over"); command printed every time I send String to Server. Also I have System.out.print("check" + buf[0]); printed out from Server many times. So that is why I think that there is something wrong with Client side.
And my Client side doesn't throw any Exceptions... but I noticed that Client side never passed the while loop. It get stuck there.
I don't know what you mean by "my client side is not good", and I can't see any obvious errors that would stop it working entirely. If you could tell us what happens, that would help.
Meanwhile there are a couple of things wrong with the code you have presented:
You are violating industry accepted coding standards with names such as "BuffIn", "IN", "IPaddresa" and so on. All variable names in Java must start with a lowercase letter.
If you do this in private code that is your business. But if you are going to show your Java code to other people, you should conform to the standards. (And posting your code on SO is showing it to other people ...)
Since you are always trying to read a whole buffer's worth of data, replace in.read(buf, 0, bytecount) with in.read(buf).
There is no value in using a BufferedInputStream or BufferedOutputStream if you are only going to do large read or write calls on it. Even more so if you tell the stream to use the same size buffer as your the byte[] you are reading / writing.
Both your client and server side code could leak file descriptors. On the server-side it could leak in. On the client side, any or all of the streams' file descriptors could leak.

Sending Files Through Stream

Here is what I have right now.
Receiver:
public static void read(Socket socket, ObjectInputStream in) {
try {
String separator = in.readUTF();
while (in.readByte() == -3) {
String path = in.readUTF().replaceAll(separator, System.getProperty("file.separator"));
File file = new File(new File(path).getParent());
if (!file.exists()) {
file.mkdirs();
}
FileOutputStream fos = new FileOutputStream(path);
int b = 0;
while ((b = in.readByte()) != -4) {
fos.write(b);
}
fos.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
Sender:
public static void send(String[] path) {
Socket socket;
try {
socket = new Socket(ip, port);
socket.setKeepAlive(true);
} catch (UnknownHostException e) {
return;
} catch (IOException e) {
return;
}
try {
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
out.writeUTF(Devbox.getSeparator());
for (String s : path) {
send(s, out);
out.writeByte(-2);
}
out.close();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
private static void send(String path, ObjectOutputStream out) {
File file = new File(path);
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
send(path + f.getName(), out);
}
} else {
try {
out.writeByte(-3);
out.writeUTF(path);
FileInputStream fis = new FileInputStream(file);
int b = 0;
while ((b = fis.read()) != -1) {
out.writeByte(b);
}
fis.close();
out.writeByte(-4);
} catch (Exception e) {
e.printStackTrace();
}
}
}
This is the error I get in the sender.
java.net.SocketException: Software caused connection abort: socket write error
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:109)
at java.net.SocketOutputStream.write(SocketOutputStream.java:153)
at java.io.ObjectOutputStream$BlockDataOutputStream.writeBlockHeader(ObjectOutputStream.java:1874)
at java.io.ObjectOutputStream$BlockDataOutputStream.drain(ObjectOutputStream.java:1855)
at java.io.ObjectOutputStream$BlockDataOutputStream.writeByte(ObjectOutputStream.java:1895)
at java.io.ObjectOutputStream.writeByte(ObjectOutputStream.java:760)
It points to
out.writeByte(b);
It sends about 25 files successfully, then throws this error. The file it throws it on is different each time, but it is in the same range of about 5 files. The receiver stops after one specific file which is usually a couple before the file the sender stops on. It stops because in.readByte() == -3 is false. When it happens, I got numbers like -85 and 16. I tried it on another computer, since it said something about software, and it was the exact same. Does anyone know why this is happening? I've spent a day trying to figure it out, and gotten nowhere. Any help is greatly appreciated.
Please read this answer, I don't think anything else can be said beyond that, also I don't see anything in your code which will close the connection.
From OTN Discussion
WinSock description: The error can occur when the local network system
aborts a connection. This would occur if WinSock aborts an established
connection after data retransmission fails (receiver never
acknowledges data sent on a datastream socket).
TCP/IP scenario: A connection will timeout if the local system doesn't
receive an (ACK)nowledgement for data sent. It would also timeout if a
(FIN)ish TCP packet is not ACK'd (and even if the FIN is ACK'd, it
will eventually timeout if a FIN is not returned).
It seems to happen more with WindowsXP and it seems also to be
possibly related to Windows firewall settings. In any case the salient
point is that the abort has originated inside the local machine.
Use a BufferedInputStream to read the FileInputStream in the send method.
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
while ((b = bis.read()) != -1) {
out.writeByte(b);
}
Also try a complementary BufferedOutputStream
BufferedInputStream bos = new FileOutputStream(path);
int b = 0;
while ((b = in.readByte()) != -4) {
bos.write(b);
}
In addition, you need to flush out your connections when the file finishes transfer.
out.flush;

Send byte array through socket between perl and Java

I have a big issue with my java client.
Server: perl, with IO::Socket;
$n is the socket
sub listen{
while(1){
my $n = $_[0]->accept();
my $thread;
my $thread2;
$thread = threads->create('talk', $n);
$thread2 = threads->create('recu', $n);
}
When a client send a message to the server
sub talk{
my $n = $_[0];
while(<$n>){
print $n $_;
}
In 'talk' the server send the client message back to the client
Client: Java, in a Thread
I send a byte array ->
static DataOutputStream os;
...
public static void handleMsg(byte [] b){
try {
os.write(b);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
->
<pre><code>
byte[] buff = new byte[]{10,4};
ThreadListen.handleMsg(buff);
</code></pre>
I receive in the run() method only the first byte in the array (10)
<pre><code>
public void run(){
DataInputStream in = null;
try{
in = new DataInputStream(s.getInputStream());
} catch (IOException e) {
System.out.println("in or out failed");
System.exit(-1);
}
while(true){
try{
byte[] buff = new byte[6];
int b = in.read(buff, 0, buff.length);
}catch (IOException e) {
System.out.println("Read failed");
System.exit(-1);
}
}
}
If I try to send
byte[] buff = new byte[]{50,4};
ThreadListen.handleMsg(buff);
I receive nothing!!!
Did i missed something, I assume that if I send
byte[] buff = new byte[]{50,4};
I should receive 50,4 :)
Thanks,
Your Perl code is doing <$n> so it's fetching data line-by-line. since 10 is the line-feed character, receiving a {10,4} sequence means it gets an empty line (which it prints back) and then a character 4. Even if it receives that (some debug messages in the Perl code would help), the print back to the socket may not complete if the socket is block-buffered (likely the default) without flushing the socket filehandle.
You may want to use read explicitly (instead of readline implicitly, which as the name suggests reads up to the end of the line, or EOF) to read from the socket.

Categories

Resources