OutputStream.write() successful but the data is not delivered [closed]
Asked Answered
A

2

9

I have a very strange behaviour during writing to a socket. In my mobile client I'm using a socket which is initialized as follows:

private void initSocket()
{
    socket = new Socket();
    socket.connect(new InetSocketAddress(host, port));

    os = new DataOutputStream(socket.getOutputStream());
    is = new DataInputStream(socket.getInputStream());
}

then periodically (every 60 seconds) I read and write some data to this socket (the code here is a little bit simplified):

if(!isSocketInitialized())
{
    initSocket();
}

byte[] msg = getMessage();

os.write(msg);
os.flush();

int bytesAvailable = is.available( );
if(bytesAvailable>0)
{
    byte[] inputBuffer = new byte[bytesAvailable];

    int numRead = is.read(inputBuffer, 0, bytesAvailable);
    processServerReply(inputBuffer, numRead);
}

And it works. But... Sometimes (very infrequently, maybe 1 or 2 times per day) my server don't receive data. My client log looks like:

Written A
Written B
Written C
Written D
Written E

and so on. But on the server side it looks like:

Received A
Received E

B,C,D data records were not received, despite of fact that on the client side it looks like all data was sent without any exceptions!

Such gaps can be small (2-3 minutes) which is not very bad, but sometimes they can be very big (1-2 hours = 60-120 cycles) and it is really a problem for my customers.

I really have no idea what can be wrong. The data seems to be sent by client, but it never arrives on the server side. I've checked it also with a proxy.

I have only logs and I can't reproduce this issue (but it happens to my customer more then one time every day) and in logs sometimes I see that the connection is broken with an Exception "sendto failed: ECONNRESET (Connection reset by peer)". After that the program closes the socket, reinitializes it:

// close
is.close();
os.close();
socket.close();

// reinitialize
initSocket();

and tries to write the data again as described above. Then I see the problem: connection established, writing successful, but NO DATA arrived on the server!

May be it has something to do with ECONNRESET may be not, but I want to mention this because may be it is important.

I would be very grateful for any ideas and tips.

P.S. Maybe it plays some role: the client code runs on an Android mobile device which is moving (it is in a car). The internet connection is established through GPRS.


UPD: I can reproduce it! At least partially (the client send A,B,C,D,E and the server receives only A). It happens every time if:

  1. The connection is established, the client reads and writes -> OK

  2. The connection is lost (I turn off my WLAN router :)), I became IOException, I close the streams and socket -> OK

  3. I turn on my router, the connection is back, I initialize the socket again, the program executes write() without exceptions, but... no data arrives at the server.

BTW since the connection is back again available() returns always 0.

Anschluss answered 25/10, 2012 at 19:52 Comment(11)
One thing that might help would be having a server that could attempt to sync with the device. If it misses the data the first time around, it can always try again - depending on how often you poll for data.Esplanade
@max, sure, you're right. But the server is closed software and I can't change the protocol.Anschluss
Couldn't you use ByteArrayOutputStream? Something like thisSomehow
@Asok yes, I can. But why? Why should it solve this problem?Anschluss
@Anschluss Valid question and ignorant comment on my behalf, trying to learn and help quickly I misread a tutorial. My apologies. Please disregard.Somehow
@Asok no problem, you shouldn't apologize :) I'm thankful for every suggestion!Anschluss
Is there a way to request server to send an acknowledgement (ack) that data was received? If server is always sending acks even when data is lost, then it's a server problem. Since it's closed software, you can't fix it. But if it will only send acks for data properly received, you can use the missing ack to decide whether to try sending again after an interval. This is probably what Whatsapp does.Hofer
@Hofer unfortunately the protocol doesn't have any ACKs. My app send messages hoping the server can receive and process them.Anschluss
That is a bad protocol. But then I thought the DataInputStream is for receiving ack messages... What does processServerReply do?Hofer
@Hofer the server has a web interface from there user can send text messages to the different clients (like mine). processServerReply interprets such messages.Anschluss
@Hofer DataInputStream is for reading primitive datatypes. It isn't bound to ACK messages in any way shape or form.Heder
A
1

The cause of this strange behavior was a not closed socket on the server side. I could reproduce it with small client and server. Both consist of couple of lines of code, which do the following:

  1. connect to the server
  2. simulate a dead spot (e.g. turn off your WiFi)
  3. close the socket on client side
  4. DON'T CLOSE the socket on server side
  5. turn on your WiFi
  6. establish a new connection from client
  7. write data to this connection

Voila! The client writes data without any errors, but the server doesn't receive it...

But if the server closes the socket too, then the server can read this data. So the solution should be to close the socket after a timeout on the server side.

Unfortunately in my case it is not viable, because the server is a proprietary third-party software.

Anschluss answered 19/11, 2012 at 18:19 Comment(1)
So there is something seriously wrong with the server. Its read on the original connection should time out or block forever or get an EOS which causes it to close that connection; and it should read new data from the new connection successfully. If it doesn't, it is single-threaded or has some other concurrency problem. I don't see why you accepted this as an answer to your own question when it doesn't answer it at all, just clarifies the error situation in which it occurs. Not an answer.Heder
H
2

Get rid of the available() test. If the peer is suppose to reply, just read it. Otherwise sometimes you will be reading the reply and sometimes you won't, and you will get out of sync. There are very few correct uses of available(), and this isn't one of them.

Heder answered 25/10, 2012 at 22:23 Comment(5)
thank you for suggestion, I'm rewriting the code so that I can read data permanently without to block writing (BTW can you recommend some reading about threads and networking?) but... can you please explain how can your suggestion help to solve the problem? I think I'm doing something wrong on closing or on initialization. So the socket is "open" and "connected", I can "write" etc but no data is delivered to the server.Anschluss
@Anschluss Selectively reading the response according to how fast it arrived cannot possibly be correct, can it? What you are doing now will cause every kind of problem imaginable, from 'missing data' that you just didn't read, to write blocks, to 'connection reset by peer'. Let us know what happened when you tried it.Heder
I've rewritten my communication again. And now I have an extra thread for reading. So I don't use available() now. But it didn't help. I have the same problem.Anschluss
I don't agree that this isn't a correct use of available() (the part where Valelik uses it to determine the size of the byte[] array definitely is wrong though). If you need to read from multiple (perhaps unbounded) number of streams and don't want to create potentially infinite threads to serve them the available() > 0 seems to me like a nice way to do it - it is similar to the select() methodology in C. However you have to remember to sleep() between iterations to avoid running your software on 100% cpu load all the time.Fowl
@Fowl What you have just described is a different use of available(). This one consists of only reading the reply if it arrives immediately, which will infallibility get out of sync some time.Heder
A
1

The cause of this strange behavior was a not closed socket on the server side. I could reproduce it with small client and server. Both consist of couple of lines of code, which do the following:

  1. connect to the server
  2. simulate a dead spot (e.g. turn off your WiFi)
  3. close the socket on client side
  4. DON'T CLOSE the socket on server side
  5. turn on your WiFi
  6. establish a new connection from client
  7. write data to this connection

Voila! The client writes data without any errors, but the server doesn't receive it...

But if the server closes the socket too, then the server can read this data. So the solution should be to close the socket after a timeout on the server side.

Unfortunately in my case it is not viable, because the server is a proprietary third-party software.

Anschluss answered 19/11, 2012 at 18:19 Comment(1)
So there is something seriously wrong with the server. Its read on the original connection should time out or block forever or get an EOS which causes it to close that connection; and it should read new data from the new connection successfully. If it doesn't, it is single-threaded or has some other concurrency problem. I don't see why you accepted this as an answer to your own question when it doesn't answer it at all, just clarifies the error situation in which it occurs. Not an answer.Heder

© 2022 - 2024 — McMap. All rights reserved.