×

如何在 iOS 上避免 SIGPIPE 信号导致的 crash (Avoiding SIGPIPE signal crash in iOS)

96
likid1412
2015.12.05 22:41* 字数 615

Update: 2015/12/8 补充参考链接

ps:翻译自 APPLE 文档,最后会附上连接和原文

当使用 socket 进行网络连接时,如果连接中断,在默认情况下,你的 process 会收到一个 SIGPIPE 信号。如果你没有处理这个信号,app 会直接 crash!!!

有两种方法可以解决这个问题,任选其一:

  • 在全局范围内忽略这个信号
signal(SIGPIPE, SIG_IGN);

需要注意的是,这个方法是全局通用的,所有的 SIGPIPE 信号都将被忽略

  • 在一开始的时候设置 socket 不要发送 SIGPIPE 信号
/// sock 就是设置不发送 `SIGPIPE` 信号的 socket 变量
int value = 1;
setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &value, sizeof(value));

附上 APPLE 原文(ref1):

Use POSIX sockets efficiently (if at all). If you are using POSIX sockets directly:

  • Handle or disable SIGPIPE.

When a connection closes, by default, your process receives a SIGPIPE signal. If your program does not handle or ignore this signal, your program will quit immediately. You can handle this in one of two ways:

  • Ignore the signal globally with the following line of code:
signal(SIGPIPE, SIG_IGN);
  • Tell the socket not to send the signal in the first place with the following lines of code (substituting the variable containing your socket in place of sock):
int value = 1;
setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &value, sizeof(value));

For maximum compatibility, you should set this flag on each incoming socket immediately after calling accept in addition to setting the flag on the listening socket itself.


下面一段提到使用 GCD 时什么时候设置 SO_NOSIGPIPE 的时机(ref2)

Handling Events with Grand Central Dispatch

GCD allows you to perform operations asynchronously, and provides an event queue mechanism for determining when to read data from the socket. After creating the listening socket, a GCD-based server should:

  1. Call dispatch_source_create to create a dispatch source for the listening socket, specifying DISPATCH_SOURCE_TYPE_READ as the source type.
  2. Call dispatch_source_set_event_handler (or dispatch_source_set_event_handler_f and dispatch_set_context) to set a handler that gets called whenever a new connection arrives on the socket.
  3. When the listen socket handler is called (upon a new connection), it should:
    • Call accept. This function fills a new sockaddr structure with information about the connection and returns a new socket for that connection.
      If desired, call ntohl(my_sockaddr_obj.sin_addr.s_addr) to determine the client’s IP address.
    • Call dispatch_source_create to create a dispatch source for the client socket, specifying DISPATCH_SOURCE_TYPE_READ as the source type.
    • Call setsockopt to set the SO_NOSIGPIPE flag on the socket.
    • Call dispatch_source_set_event_handler (or dispatch_source_set_event_handler_f and dispatch_set_context) to set a handler that gets called whenever the state of the connection changes.
  4. In the client socket handler, call dispatch_async or dispatch_async_f and pass a block that calls read on the socket to grab any new data, then handle that data appropriately. This block can also send responses by calling write on the socket.

ref:

  1. Avoiding Common Networking Mistakes: https://developer.apple.com/library/ios/documentation/NetworkingInternetWeb/Conceptual/NetworkingOverview/CommonPitfalls/CommonPitfalls.html
  2. Using Sockets and Socket Streams: https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/UsingSocketsandSocketStreams.html#//apple_ref/doc/uid/CH73-SW1
  3. Ignore SIGPIPE signal on iOS
日记本
Web note ad 1