开发者

Writing a webserver in objective c-- getting a SIGPIPE call when uploading larger files

I am writing a webserver for a music sharing app ... when I have a large file (i.e an mp3) this does not work. It crashes on SIGPIPE error code. The header I am sending has "Connection: close" -- but I assumed this would wait until after the download finishes to close the connection. I know this should probably be forked into a thread but for testing i want to get it working synchronously.

NSData *fileData =[NSData dataWithContentsOfFile:filePath];

CFHTTPMessageRef response =
CFHTTPMessageCreateResponse(
                            kCFAllocatorDefault, 200, NULL, kCFHTTPVersion1_1);
CFHTTPMessageSetHeaderFieldValue(
                                 response, (CFStringRef)@"Content-Type", (CFStringRef)@"audio/mpeg");
CFHTTPMessageSetHeaderFieldValue(
                                 response, (CFStringRef开发者_StackOverflow中文版)@"Connection", (CFStringRef)@"close");
CFHTTPMessageSetHeaderFieldValue(
                                 response,
                                 (CFStringRef)@"Content-Length",
                                 (CFStringRef)[NSString stringWithFormat:@"%ld", [fileData length]]);
CFDataRef headerData = CFHTTPMessageCopySerializedMessage(response);

@try
{
    [fileHandle writeData:(NSData *)headerData];
    [fileHandle writeData:fileData];
}@catch (NSException *exception)
{
    // Ignore the exception, it normally just means the client
    // closed the connection from the other end.
}


A web server has to cope with the case where the client closes the connection early -- perhaps it has enough, or the page load or download was canceled. This can happen even when the server is written correctly. Have you looked at a packet capture of the conversation?


You should check out How to prevent SIGPIPEs (or handle them properly).

This is a bit of a rehash, but basically use signal(SIGPIPE, SIG_IGN); when your app first launches. This will prevent the SIGPIPE from being signaled (i.e. your app won't crash). You usually do this when you want to handle the problem locally. You can check locally by doing something like:

if ([fileHandle writeData:(NSData *)headerData] > 0 &&
    [fileHandle writeData:fileData] > 0)
{
   // writes succeeded, ...
}
else
{
   // write failed for some reason
}

You can additionally check for errors using NSStream's streamError or streamStatus properties.


Though you really should deal with SIGPIPE. But I guess why you get it is because you didn't check request method before sending data.
It is very possible that a downloader will send a HEAD request first to get the content length and other information.

When you get a HEAD request, you should not call [fileHandle writeData:fileData]. I guess you are using the HTTPServer code from here. If so, you can just do this:

if (![requestMethod isEqualToString:@"HEAD"] && fileData)
{
  [fileHandle writeData:fileData];
}

Other than that, you may also need to cope with range request to avoid SIGPIPE. And maybe other stuff. It is not easy to write a HTTP server to serve real world requester.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜