Search code examples
iostwistedgcdasyncsocket

Access GCDAsyncSocket read queue


I'm using GCDAsyncSocket library for my iOS messaging app with Twisted Server in the backend. Everything is setup fine and works fine, but I found out about a peculiar situation when it doesn't work fine. So, my server is architectured in such a way that when the user is offline, all his message are cached on server and when he's detected online, the server sends to him all his cached pending messages at once.

Now, GCDAsyncSocket uses internal read and write queues, so here, those sent pending-messages by the server are queued by the read queue until they're read by the delegate method -(void)socket:didReadData:withTag: Now, in my app, this delegate method processes the incoming messages and displays them in a tableview one by one. So, the point of problem is that whenever the delegate method is reading from the queue, if you suspend the app, all the unprocessed messages in the read queue are lost. So to counter this, I want to access the read queue so that I can save its contents before app suspension so that I can recover it when app is opened again.

Note: As per my understanding, the lost messages are from the queue and not the server because the server shows "all messages sent" even before the delegate reaches half of the messages while processing. And after that if suspended, then this problem persists.

So am I doing it the wrong way? Or is there any way to access the queue?


Solution

  • You could consider adding an acknowledgement step to your protocol. Instead of letting the server send all queued messages and then immediately forget them, you could have it remember them until the client sends back a per-message acknowledgement.

    This way, no matter what the client does between receiving a particular message and displaying it to the user, that message will always be available for re-delivery from the server. Only after the client has viewed (and therefore acknowledged) the message will the server forget it (and thus not re-transmit it to the client the next time the client shows up).

    You can do this pretty easily by assigning unique identifiers to messages. These might be random UUIDs or - if your message deliver must always be in order - sequence numbers. Sequence numbers are convenient (if they make sense for your application) because by acknowledging a single message sequence number your client can indicate that it has processed several messages (because if it always processes them in order then any message with an earlier sequence number must have been processed too).

    The failure mode for this strategy is the opposite as the failure mode for your current strategy. Now, when the phone suspends before a message is pulled from the receive queue, it is lost. Using acknowledgements, the message cannot be lost until it has been processed (displayed). However, you might display the message and then fail to send the acknowledgement somehow. This will lead to a message being displayed twice.

    You can narrow the window for this failure by also recording information on the phone about which messages have been processed (displayed). If the phone receives a message from the server that it knows it has already displayed then it can skip displaying it and just re-send the acknowledgement to allow the server to forget it.

    This reduces the failure window to just the time between when you display a message and when you manage to record locally that that message has been displayed (or the opposite order, if you prefer the failure mode where messages are lost rather than where they are displayed twice).