Search code examples
iosblockingberkeley-sockets

In iOS, why doesn't socket recv fail when the underlying network changes?


In iOS when I've got a blocking socket with a timeout set using SO_RCVTIMEO. When I change the WiFi network that the device is connected to, the calls to recv will timeout appropriately, but errno will be reported as EWOULDBLOCK and recv returns -1.

This causes my outer processing loop attempt to access the socket again (as it seems there is just no data available right now), when realistically this is impossible at this point as the device is now connected to a different network endpoint.

If I just kill the network altogether, several repeated calls to recv will eventually fail with ETIMEDOUT, after many EWOULDBLOCK.

Why does this occur? Shouldn't the socket connection be broken and fail with ENETRESET or a similar errno?

If I don't have the SO_RCVTIMEO, then the socket will block forever when the WiFi network changes. Also no bueno.

EDIT: I would think that I could just enable SO_KEEPALIVE on the local socket to detect when the remote socket stops responding due to the underlying network endpoint changing. This doesn't seem to work either.


Solution

  • I'll post my own answer here. It turns out that when using keepalive, it must first be enabled for the socket level SOL_SOCKET and also set the keepalive interval at the TCP layer (IPPROTO_TCP level with TCP_KEEPALIVE), as the default is usually 7200 seconds (2 hours).

    int on = 1;
    int delay = 120;
    setsockopt(socket_handle, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on));
    setsockopt(socket_handle, IPPROTO_TCP, TCP_KEEPALIVE, &delay, sizeof(delay));
    

    I came across the syntax according to a similar post.

    Is it possible to activate TCP keepalive on Apple iOS devices