我如何在我的NSFIleHandle处理中捕获EPIPE?

我在我的iOS应用程序中遇到了EPIPE问题,并没有被@ try / @ catch / @ finally块捕获。 我怎么能捕捉到这个信号(SIGPIPE,可能)…

我已经在我的应用程序中构build了一个“Web代理”,它将处理某些types的URL – 在这种错误情况下,似乎远程端(也在我的应用程序中,但隐藏在iOS库中)closures了套接字的末尾。 我没有收到通知(我应该吗?有什么我应该注册的NSFileHandle可能有帮助吗?)。

我已经把这个代理放在了Matt Gallagher所提供的HTTPServer上(可以在这里find ),问题在于他放在一起的HTTPRequestHandler类的子类。 这是代码(这个代码和基类中的startResponse方法是等价的):

 -(void)proxyTS:(SSProxyTSResource *)proxyTS didReceiveResource:(NSData *)resource { NSLog(@"[%@ %@]", NSStringFromClass([self class]), NSStringFromSelector(_cmd)); CFHTTPMessageRef response = CFHTTPMessageCreateResponse(kCFAllocatorDefault, 200, NULL, kCFHTTPVersion1_1); CFHTTPMessageSetHeaderFieldValue(response, (CFStringRef)@"Content-Type", (__bridge CFStringRef)s_MIMEtype); CFHTTPMessageSetHeaderFieldValue(response, (CFStringRef)@"Connection", (CFStringRef)@"close"); CFHTTPMessageSetBody(response, (__bridge CFDataRef)resource); CFDataRef headerData = CFHTTPMessageCopySerializedMessage(response); @try { NSLog(@" -> writing %u bytes to filehandle...",[((__bridge NSData *)headerData) length]); [self.fileHandle writeData:(__bridge NSData *)headerData]; } @catch (NSException *exception) { // Ignore the exception, it normally just means the client // closed the connection from the other end. } @finally { NSLog(@" *ding*"); CFRelease(headerData); CFRelease(response); [self.server closeHandler:self]; } } 

以下是当控制台日志崩溃时显示的内容:

 Jan 15 14:55:10 AWT-NoTouch-iPhone-1 Streamer[1788] <Warning>: [SSProxyTSResponseHandler proxyTS:didReceiveResource:] Jan 15 14:55:10 iPhone-1 Streamer[1788] <Warning>: -> writing 261760 bytes to filehandle... Jan 15 14:55:11 iPhone-1 com.apple.launchd[1] (UIKitApplication:com.XXX.Streamer[0xf58][1788]) <Warning>: (UIKitApplication:com.XXX.Streamer[0xf58]) Exited abnormally: Broken pipe: 13 

看起来,因为另一端closurespipe道的write()失败,所以如果有人能指出我如何可以发现它已经closures,而不是试图写入数据或任何将使它不会崩溃我的程序会是非常有帮助的。

解决了SIGPIPE崩溃的直接问题。 我对这个解决scheme并不完全感兴趣,但至less应用程序不会崩溃。 目前还不清楚它是否正确工作,但似乎performance得好一些。

我已经通过进一步检查发生了什么来解决这个问题。 在做一些研究时,我发现也许我应该使用NSFileHandle的writeabilityHandler属性来安装一个块来完成写作。 我没有完全按照这种方式销售(这让我觉得很复杂),但它可能会有所帮助。

可写性处理程序解决scheme:

在一些关于writeabilityHandler网页search中,我偶然发现了Bert Leung的博客文章,介绍了他在类似领域中遇到的一些问题。 我把他的代码和修改它如下,用这个代码replace上面的@try/@catch/@finally块:

 self.pendingData = [NSMutableData dataWithData:(__bridge NSData *)(headerData)]; CFRelease(headerData); CFRelease(response); self.fileHandle.writeabilityHandler = ^(NSFileHandle* thisFileHandle) { int amountSent = send([thisFileHandle fileDescriptor], [self.pendingData bytes], [self.pendingData length], MSG_DONTWAIT); if (amountSent < 0) { // errno is provided by system NSLog(@"[%@ %@] Error while sending response: %d", NSStringFromClass([self class]), NSStringFromSelector(_cmd), errno); // Setting the length to 0 will cause this handler to complete processing. self.pendingData.length = 0; } else { [self.pendingData replaceBytesInRange:NSMakeRange(0, amountSent) withBytes:NULL length:0]; } if ([self.pendingData length] == 0) { thisFileHandle.writeabilityHandler = nil; // Hack to avoid ARC cycle with self. I don't like this, but... [[NSNotificationCenter defaultCenter] postNotification:self.myNotification]; } }; 

这工作得很好, 没有解决问题。 我还在得到SIGPIPE / EPIPE。

SIGPIPE不见了!

这并不令人意外,因为它和前面的writeData:但是使用send()来代替。 关键的区别在于使用send()允许errno 。 这实际上是相当有帮助的 – 我得到了几个错误代码(在errno中),例如54(通过对等方重置连接)和32(断开的pipe道)。 54年代没有问题,但是32年代产生了SIGPIPE / EPIPE。 然后,我明白了 – 也许我应该忽略SIGPIPE。

考虑到这个想法,我在application:didFinishLaunchingWithOptions:添加了一些钩子到我的UIApplicationDelegateapplication:didFinishLaunchingWithOptions:

 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. [self installSignalHandlers]; if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) { ... 

applicationWillTerminate: ::

 - (void)applicationWillTerminate:(UIApplication *)application { // Saves changes in the application's managed object context before the application terminates. [self removeSignalHandlers]; [self saveContext]; } -(void)installSignalHandlers { signal(SIGPIPE,SIG_IGN); } -(void)removeSignalHandlers { signal(SIGPIPE, SIG_DFL); } 

现在至less应用程序不会崩溃。 目前尚不清楚它是否正确运行,但似乎是行为正常。

我也切换回@try/@catch/@finally结构,因为它更直接。 此外,在忽略SIGPIPE之后, @catch块不会被触发。 现在,我正在loggingexception,但只有这样我才能看到它正在工作。 在发布的代码中,该日志将被禁用。