1

Problem:

I'm making an app that has instant messaging. For the most part, the messages are sent and received without any issues. But after some period of time, somewhere between 10 and 40 minutes, the messages no longer reach the server if the client's socket hasn't been used in a while. For example, if I chat with someone, leave the app open, take a nap, and come back to chat again, then the messages won't send. I am 99% sure it is not a problem on the server side because I print out everything it receives (and it's not receiving anything) and it works fine if I log in with another phone.

What I've tried:

At first I thought it was a timeout issue on the client side because on the server side I kept getting an error saying, "Connection reset by peer", somewhere in that inverval of 10-40 minutes. I (almost) resolved this by using Java.net.socket's connect() method with a timeout value of 0 which gives an infinite timeout:

clientSocket = new Socket();
clientSocket.connect( new InetSocketAddress(ServerInfo.IP, ServerInfo.PORT_NUMBER), 0 ); 

I rarely get the "Connection reset by peer" error message now, but the mysterious problem of messages not sending after some time goes by still remains.

Code:

This is my sendMessage function which always logs "SOCKET OPERATOR SENDING MESSAGE: 'message'":

public boolean sendMessage(String message) 
{                                                       
       PrintWriter out = null;

        try 
        { 
            out = new PrintWriter( clientSocket.getOutputStream(), true );
            Log.i( "MY_TAG", "SOCKET OPERATOR SENDING MESSAGE: " + message );

        } 
        catch (IOException e) 
        {
            e.printStackTrace();
            Log.i( "MY_TAG", "SOCKET OPERATOR FAILEDD TO SEND MESSAGE WITH EXCEPTION: " + e.getMessage() );

            return false;                   
        }


        out.println(message);

        return true;            
}

Question:

What on earth could possibly be the problem or how could I further debug this?

Kacy
  • 3,330
  • 4
  • 29
  • 57

3 Answers3

3

Logging at the point you do is futile. Nothing has happened yet.

If the connection is dead, sooner or later a send will cause an IOException: connection reset. But not the first time, due to socket buffering.

And when you do get this exception, don't just return false. Close the connection.

HOWEVER the problem here is the PrintWriter. It swallows exceptions. See the Javadoc. Either call checkError(), which returns a boolean indicating whether there has been an exception, or, better still, don't use PrintWriter at all: use BufferedWriter.write() and .newLine(), and .flush(), all of which can throw IOExceptions. This is better because you can see what the exception actually was. You'll have to move all that into the try block of course, ahead of the presently misleading log message.

And don't use a new PrintWriter or BufferedWriter per message. Use the same one for the life of the socket.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • Wow I didn't realize the issue was just that PrintWriter hides I/O exceptions. I think this was most likely the problem. (Although I'm not willing to go remove my heartbeat function and wait a half hour to verify). Thank you. I'll be sure to follow all your advice. – Kacy Aug 13 '15 at 08:44
  • Oh but what do you suspect was causing the connection to drop in the first place? I set an infinite timeout on the client side. Was it the server side even though it was taking about a half hour to stop working? – Kacy Aug 13 '15 at 08:50
  • No idea, sorry, could be a NAT mapping timeout as you suggested. – user207421 Aug 13 '15 at 08:55
1

Before sending any message you must check if the connection still exists b/w client and server.

There are chances that if net connection becomes slow or is off in between, the connection will break or sometimes it becomes a dead connection from the server side because there was no communication b/w both.

you may try two things:

1. Make a general method which on sending message checks if: the connection exists then just send the message else : first make the connection and send that message.

We faced the same problem in Websockets and used the same startegy.

2. Ask server side team to keep sending some heartbeat packets from their side, this way your connection will not be dead.

For us, first strategy worked out and is more better.

Give it a try. :)

Harish Vats
  • 662
  • 6
  • 21
  • I figured a heartbeat would work, but wanted to understand the problem before I implemented it. Thanks for the suggestions. You got me googling in the right direction, and I think the problem isn't caused by the client device or the server but rather what's in between them. One of the answers on this post mentions that: http://stackoverflow.com/questions/865987/do-i-need-to-heartbeat-to-keep-a-tcp-connection-open He mentions a firewall, but I'm thinking it could be my NAT router having its own timeout mechanism. – Kacy Aug 12 '15 at 08:04
  • you may use socket's readyState() method to check if the connection is established or disconnected(dead). – Harish Vats Aug 12 '15 at 09:02
  • 1
    Neither of those methods exist in `Java.net.socket`'s documentation.The closest thing it has to what you're describing is the isConnected() method but that always returns true once the socket is connected regardless if the connection has been lost. – Kacy Aug 12 '15 at 14:35
  • There is no such thing in TCP as checking the state of the connection. The only way you can detect a dead connection is to try to send on it. The `readyState()` method you mention in comments is entrely fictitious. – user207421 Aug 12 '15 at 23:05
  • @EJP I worked on websockets,, so on its reference I told the method name. We faced the same problem. Have a look isReady method do exists there: https://github.com/nathanstilwell/SocketWrench And please note I stated you "may" use. It was just a suggestion from my side. Of course she had to some search for the exact thing. – Harish Vats Aug 13 '15 at 07:10
  • 1
    The OP isn't talking about Websockets, and mentioning a method name without the class it belongs to or a link to its Javadoc is futile. There isn't such a method in the entire JDK. 'Some search' is an understatement. As far as I have been able to determine, there is no such method. The link you provided (i) does not contain a method called `readyState()`, (ii) refers to a Javascript object, and (iii) is therefore entirely irrelevant. – user207421 Aug 13 '15 at 08:30
0

I believe the problem didn't necessarily lie with the client device or with the server, but rather with what's between those 2 things, my NAT router. The router is most likely dropping mappings in its table due to inactivity.

My solution is to use a heartbeat function (along with modifying my use of PrintWriter as EJP suggested), sending from the client to the server every 45 seconds. I'll most likely randomize the time somewhat just to handle the case in which a ton of people log in at the same time and affect the server's performance.

Kacy
  • 3,330
  • 4
  • 29
  • 57
  • 1
    There no 'unaware' about it. A send requires an acknowledgement, and, if it doesn't arrive, a retry, and if all that fails the connection will eventually be reset by the sending end. – user207421 Aug 13 '15 at 00:13
  • @EJP A connection is not reset just because one packet doesn't result in the client getting an ACK back from the server. That would mean every time an ACK gets lost/dropped in the network, regardless of a working connection, that connections would be reset. And that just isn't true. It is true however that translations time out in NAT routers. – Kacy Aug 13 '15 at 08:32
  • If a packet fails to elicit an acknowledgement *after the appropriate number of retries and timeouts* the connection will indeed be reset. I haven't said anything about single ACKs being lost or NAT translations not timing out. Don't put words into my mouth. – user207421 Aug 13 '15 at 08:44
  • You're right! The connection does reset on the sender's end if it sends a message and never receives an ACK for it. I edited my answer. Thanks for clearing things up. – Kacy Aug 15 '15 at 01:14