mirror of https://github.com/grpc/grpc.git
parent
f613be8e82
commit
965a015b05
2 changed files with 86 additions and 53 deletions
@ -1,53 +0,0 @@ |
||||
|
||||
# gRPC iOS Network Transition Behaviors |
||||
On iOS devices, network transitions happen frequently. A device may have |
||||
multiple interfaces to connect to network, and these interfaces may become |
||||
broken or alive at any time. This document describes how these network changes |
||||
should be handled by gRPC and current issues related. |
||||
|
||||
## gRPC iOS with TCP Sockets |
||||
### Model |
||||
We classify network connectivity of the device at a given time in three |
||||
categories: WiFi, cellular, and none. The network connectivity information is |
||||
obtained from `SCNetworkReachability` API and the category is determined by |
||||
`SCNetworkReachabilityFlags` as follows: |
||||
|
||||
| Reachable | ConnectionRequired | IsWAAN | **Category** | |
||||
|:---------:|:------------------:|:------:|:------------:| |
||||
| 0 | X | X | None | |
||||
| X | 1 | X | None | |
||||
| 1 | 0 | 0 | WiFi | |
||||
| 1 | 0 | 1 | Cellular | |
||||
|
||||
Whenever there is a transition of network between these three categories, the |
||||
previous channels is assumed to be broken. If there is an unfinished call, the |
||||
call should return with status code `UNAVAILABLE`. All active gRPC channels |
||||
will be destroyed so that any new call will trigger creation of new channel |
||||
over new interface. In addition to that, when a TCP connection breaks, the |
||||
corresponding channel should also be destroyed and calls be canceled with |
||||
status code `UNAVAILABLE`. |
||||
|
||||
### Known issues |
||||
There are several issues related to BSD sockets known to us that causes |
||||
problems. |
||||
|
||||
* TCP socket stalls but does not return error when network status transits from |
||||
Cellular to WiFi. This problem is workarounded by |
||||
[ConnectivityMonitor](https://github.com/grpc/grpc/blob/master/src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.m). |
||||
* TCP socket stalls but does not return error when WiFi reconnects to another |
||||
hotspot while the app is in background. |
||||
|
||||
If you encounter these problems, the best solution is to switch to CFStream |
||||
implementation which eliminates all of them. |
||||
|
||||
## gRPC iOS with CFStream |
||||
gRPC iOS with CFStream implementation uses Apple's networking API to make |
||||
connections. It resolves the issues above that is known to TCP sockets on iOS. |
||||
Users are recommended to use this implementation rather than TCP socket |
||||
implementation. With CFStream implementation, a channel is broken when the |
||||
underlying stream detects an error or becomes closed. The behavior of streams |
||||
in CFStream are not documented by Apple, but our experiments show that it is |
||||
very similar to the model above, i.e. the streams error out when there is a |
||||
network connetivity change. So users should expect channels to break when the |
||||
network transits to another state and pending calls on those channels return |
||||
with status code `UNAVAILABLE`. |
@ -0,0 +1,86 @@ |
||||
|
||||
# gRPC iOS Network Transition Behaviors |
||||
Network connectivity on an iOS device may transition between cellular, WIFI, or |
||||
no network connectivity. This document describes how these network changes |
||||
should be handled by gRPC and current known issues. |
||||
|
||||
## Expected Network Transition Behaviors |
||||
The expected gRPC iOS channel and network transition behaviors are: |
||||
* Channel connection to a particular host is established at the time of |
||||
starting the first call to the channel and remains connected for future calls |
||||
to the same host. |
||||
* If the underlying connection to the remote host is broken, the channel is |
||||
disconnected and enters TRANSIENT\_FAILURE state. |
||||
* A channel is broken if the channel connection is no longer viable. This |
||||
happens when |
||||
* The network interface is no longer available, e.g. WiFi or cellular |
||||
interface is turned off or goes offline, airplane mode turned on, etc; |
||||
* The underlying TCP connection is no longer valid, e.g. WiFi connects to another hotspot, cellular data switched from LTE to 4G, etc; |
||||
* A network interface more preferable by the OS is valid, e.g. WiFi gets connected when the channel connects via cellular. |
||||
* A channel in TRANSIENT\_FAILURE state attempts reconnection on start of the |
||||
next call to the same host, but only after a certain backoff period (see |
||||
corresponding |
||||
[doc](https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md)). |
||||
During the backoff period, any call to the same host will wait until the |
||||
first of the following events occur: |
||||
* Connection succeeded; calls will be made using this channel; |
||||
* Conncetion failed; calls will be failed and return UNAVAILABLE status code; |
||||
* The call's deadline is reached; the call will fail and return |
||||
DEADLINE\_EXCEEDED status code. |
||||
|
||||
## Implementations |
||||
### gRPC iOS with TCP Sockets |
||||
gRPC's default implementation is to use TCP sockets for networking. It turns |
||||
out that although Apple supports this type of usage, it is [not recommended by |
||||
Apple](https://developer.apple.com/library/archive/documentation/NetworkingInternetWeb/Conceptual/NetworkingOverview/SocketsAndStreams/SocketsAndStreams.html) |
||||
and is also flawed. |
||||
|
||||
#### TCP Sockets Issues |
||||
The TCP sockets on iOS is flawed in that it does not reflect the viability of |
||||
the channel connection. Particularly, we found the following issues related to |
||||
TCP sockets: |
||||
* When a TCP sockets connection is established on cellular data and WiFi |
||||
becomes available, the TCP socket neither return an error event nor continue |
||||
sending/receiving data on it, but still accepts write on it. |
||||
* The TCP sockets does not report certain events that happens in the |
||||
background. When a TCP connection breaks in the background for the reason |
||||
like WiFi connects to another hotspot, the socket neither return an error nor |
||||
continue sending/receiving data on it, but still accepts write on it. |
||||
|
||||
#### gRPC iOS resolutions |
||||
We introduced |
||||
[`ConnectivityMonitor`](https://developer.apple.com/library/archive/documentation/NetworkingInternetWeb/Conceptual/NetworkingOverview/SocketsAndStreams/SocketsAndStreams.html) |
||||
in gRPC iOS library to alleviate these issues in TCP sockets, which changes the |
||||
network transition behaviors a bit. |
||||
|
||||
We classfy network connectivity state of the device into three categories based |
||||
on flags obtained from `SCNetworkReachability` API: |
||||
|
||||
| Reachable | ConnectionRequired | IsWWAN | **Category** | |
||||
|:---------:|:------------------:|:------:|:------------:| |
||||
| 0 | X | X | None | |
||||
| X | 1 | X | None | |
||||
| 1 | 0 | 0 | WiFi | |
||||
| 1 | 0 | 1 | Cellular | |
||||
|
||||
Whenever there is a transition of network between two of these categories, all |
||||
previously existing channels are assumed to be broken and are actively |
||||
destroyed. If there is an unfinished call, the call should return with status |
||||
code `UNAVAILABLE`. |
||||
|
||||
`ConnectivityMonitor` is able to detect the scenario of the first issue above |
||||
and actively destroy the channels. However, the second issue is not resolvable. |
||||
To solve that issue the best solution is to switch to CFStream implementation |
||||
which eliminates all of them. |
||||
|
||||
### gRPC iOS with CFStream |
||||
gRPC iOS with CFStream implementation uses Apple's networking API to make |
||||
connections. It resolves the issues above that is known to TCP sockets on iOS. |
||||
Users are recommended to use this implementation rather than TCP socket |
||||
implementation. The detailed behavior of streams in CFStream is not documented |
||||
by Apple, but our experiments show that it accords to the expected behaviors. |
||||
With CFStream implementation, an event is always received when the underlying |
||||
connection is no longer viable. For more detailed information and usages of |
||||
CFStream implementation, refer to the |
||||
[user guide](https://github.com/grpc/grpc/blob/master/src/objective-c/README-CFSTREAM.md). |
||||
|
Loading…
Reference in new issue