-->

我如何能赶上EPIPE我NSFIleHandle处理?(How can I catch EPIPE

2019-09-27 10:44发布

我在与我的iOS应用EPIPE一个问题,它不是在被抓@尝试/ @捕捉/ @ finally块。 我怎么能抓住这个信号(SIGPIPE,可能)...

我已经建立了一个“网络代理”到我的应用程序将处理某些类型的URL - 在这种错误下,似乎远端(也是在我的应用程序,但在iOS的库藏)关闭其插座端。 我没有得到通知(要这样呢?有什么我应该用NSFileHandle这可能有助于这里注册?)。

我基于HttpServer的马特·加拉格尔放在一起(这家代理在这里 ),而问题是在子类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

看来,由于另一端关闭了管道write()失败,所以如果有人能在我怎样可以发现它已经关闭指向我,而不是试图将数据写入或任何会令它不会崩溃我的程序,将是非常有益的。

Answer 1:

与SIGPIPE崩溃的迫在眉睫的问题就解决了。 我不完全傻傻的这个解决方案,但至少该应用程序不会崩溃。 目前尚不清楚,它的正常工作100%,但它似乎表现相当好一点。

我通过进一步检查发生了什么事情解决了这个问题。 在做一些研究,我发现,也许我应该使用NSFileHandle的writeabilityHandler财产安装块来执行写入操作。 我不完全对这种做法出售(它感到费解的我),但它可能的帮助。

可写处理程序解决方案:

在做一些网络上搜索writeabilityHandler ,我偶然在伯特梁的博客文章在一些问题上,他是在一个类似的区域有。 我把他的代码,并修改它,如下所示,替换@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进行设置。 这是非常有益的,实际上 - 我得到几个错误代码(在错误号),如54(由对等体将连接复位)和32(中断的管道)。 在54年代是很好,但32的导致SIGPIPE / EPIPE。 然后我恍然大悟 - 也许我应该只是忽略SIGPIPE。

鉴于想法,我增加了几个钩子到我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);
}

现在,至少在应用程序不会崩溃。 目前尚不清楚,它的正常工作100%,但它似乎表现。

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



文章来源: How can I catch EPIPE in my NSFIleHandle handling?