cocoasocketsasyncsocketcasyncsocket

Asyncsockets and "silent" disconnections


I've been using cocoaasyncsocket as a client to a Windows .net server using asyncsocket. I am encoding messages using ProtocolBuffers. Together, these make a great set of tools.

However recently I have noticed that if I leave the client connected to the server for a long time - many hours - when I try to make a request for data, the message appears to get sent but never arrives at the server. I'm calling this a "silent" disconnection because I'm not receiving the usual disconnection that I would if there were a network problem.

I am handling the following methods in my effort to debug, but none of them get called:

- (NSTimeInterval)onSocket:(AsyncSocket *)sock
  shouldTimeoutReadWithTag:(long)tag
                   elapsed:(NSTimeInterval)elapsed
                 bytesDone:(CFIndex)length {

- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err

- (void)onSocketDidDisconnect:(AsyncSocket *)sock

Without any notification, I'm finding this tricky to debug. The server similarly does not show any disconnections.

Would anyone be able to point me in a direction to how to further analyze this?

Many thanks.


Solution

  • If you want to know that your clients and servers are connected, you must have a heartbeat or "keepalive", i.e. an application level message which is exchanged basically saying "Are you there"/"yes".

    This is particularly true nowadays as many routers will silently drop idle TCP connections. That's not what TCP was designed for, but it is a fact of life.

    TCP has an option to send connection-layer heartbeat or keepalive with SO_KEEPALIVE, but historically there was an argument over whether this was appropriate. The designers felt that dropping a socket because of a temporary intermediate network problem was wrong. If no data was actually sent during that period, there was no reason to drop the connection. Others felt that knowing whether the connection was good was important in and of itself. What if you were expecting data but it hadn't arrived? Woudln't you want to know?

    Ultimately it depends on the application. If "no new information" is an assertion of fact which is important to your application, (for example news feeds, sales orders, market data price changes) you need to check that the "no new information" is a real "no new information" not a "connection silently dropped". That means an explicit message.

    So how often should you send it?

    That depends on a balance of things. 1a) how often do you normally get updates? 1b) What latency is acceptable/normal (e.g. if this is part of a process, other steps take typically hours, then 5-10 minutes might be acceptable). 2a) battery and 2b) data usage due to heartbeat. I suspect the effect on battery/power will be crucial, but all these need to be looked at carefully, and balanced.

    You could lose coverage at any time after all (tunnels etc). I would run the heartbeat only if you have had no updates after something between 0.5 and 5 times the average update interval. So if you expect 2 updates per minute, run heartbeats if idle for 15 seconds to 3 minutes - judge what is best. Provided the user is in the app "live" and "turns it off" when done, the battery life is not such an issue since they are using it anyway. Battery life is really an issue if you are waking the device to process the updates.