回答此问题可获得 20 贡献值,回答如果被采纳可获得 50 分。
<p>我正在尝试将联系人从用户的地址簿流式传输到我们的服务器。一次将所有联系人拉入内存可能会导致设备崩溃或无响应。我不想承担将所有联系人写入文件并上载文件的开销。我可以看到数据是通过网络发送的,但看起来格式无效。服务器无法识别请求正文。在</p>
<p>我正在从地址簿中读取联系人并将其写入NSOutputStream。此NSOutputStream通过以下代码与NSInputStream共享一个缓冲区</p>
<p><a href="https://stackoverflow.com/questions/3221030/buffering-nsoutputstream-used-as-nsinputstream">Buffering NSOutputStream used as NSInputStream?</a></p>
<pre><code>//
// NSStream+BoundPairAdditions.m
// WAControls
//
//
#import "NSStream+BoundPairAdditions.h"
#include <sys/socket.h>
static void CFStreamCreateBoundPairCompat(
CFAllocatorRef alloc,
CFReadStreamRef * readStreamPtr,
CFWriteStreamRef * writeStreamPtr,
CFIndex transferBufferSize
)
// This is a drop-in replacement for CFStreamCreateBoundPair that is necessary because that
// code is broken on iOS versions prior to iOS 5.0 <rdar://problem/7027394> <rdar://problem/7027406>.
// This emulates a bound pair by creating a pair of UNIX domain sockets and wrapper each end in a
// CFSocketStream. This won't give great performance, but it doesn't crash!
{
#pragma unused(transferBufferSize)
int err;
Boolean success;
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
int fds[2];
assert(readStreamPtr != NULL);
assert(writeStreamPtr != NULL);
readStream = NULL;
writeStream = NULL;
// Create the UNIX domain socket pair.
err = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
if (err == 0) {
CFStreamCreatePairWithSocket(alloc, fds[0], &readStream, NULL);
CFStreamCreatePairWithSocket(alloc, fds[1], NULL, &writeStream);
// If we failed to create one of the streams, ignore them both.
if ( (readStream == NULL) || (writeStream == NULL) ) {
if (readStream != NULL) {
CFRelease(readStream);
readStream = NULL;
}
if (writeStream != NULL) {
CFRelease(writeStream);
writeStream = NULL;
}
}
assert( (readStream == NULL) == (writeStream == NULL) );
// Make sure that the sockets get closed (by us in the case of an error,
// or by the stream if we managed to create them successfull).
if (readStream == NULL) {
err = close(fds[0]);
assert(err == 0);
err = close(fds[1]);
assert(err == 0);
} else {
success = CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
assert(success);
success = CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
assert(success);
}
}
*readStreamPtr = readStream;
*writeStreamPtr = writeStream;
}
// A category on NSStream that provides a nice, Objective-C friendly way to create
// bound pairs of streams.
@implementation NSStream (BoundPairAdditions)
+ (void)createBoundInputStream:(NSInputStream **)inputStreamPtr outputStream:(NSOutputStream **)outputStreamPtr bufferSize:(NSUInteger)bufferSize
{
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
assert( (inputStreamPtr != NULL) || (outputStreamPtr != NULL) );
readStream = NULL;
writeStream = NULL;
#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 1070)
#error If you support Mac OS X prior to 10.7, you must re-enable CFStreamCreateBoundPairCompat.
#endif
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && (__IPHONE_OS_VERSION_MIN_REQUIRED < 50000)
#error If you support iOS prior to 5.0, you must re-enable CFStreamCreateBoundPairCompat.
#endif
if (NO) {
CFStreamCreateBoundPairCompat(
NULL,
((inputStreamPtr != nil) ? &readStream : NULL),
((outputStreamPtr != nil) ? &writeStream : NULL),
(CFIndex) bufferSize
);
} else {
CFStreamCreateBoundPair(
NULL,
((inputStreamPtr != nil) ? &readStream : NULL),
((outputStreamPtr != nil) ? &writeStream : NULL),
(CFIndex) bufferSize
);
}
if (inputStreamPtr != NULL) {
*inputStreamPtr = CFBridgingRelease(readStream);
}
if (outputStreamPtr != NULL) {
*outputStreamPtr = CFBridgingRelease(writeStream);
}
}
@end
</code></pre>
<p>在这里,我通过处理NSOutputStream委派来构建请求主体。在</p>
^{pr2}$
<p>我在用AFNetworking来建立网络。我将请求正文流设置为NSInputStream。在</p>
<pre><code>NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
[request setHTTPMethod:@"POST"];
[request setValue:@"application/json; charset=UTF-8" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBodyStream:inputStream];
AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:request];
op.responseSerializer = [AFHTTPResponseSerializer serializer];
[op setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
NSLog(@"PROGRESS %d %lld %lld", bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
}];
[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
[self processResponse:responseObject success:success error:error log:log];
} failure:^(AFHTTPRequestOperation *operation, NSError *e) {
[self processError:e op:operation error:error log:log];
}];
[[NSOperationQueue mainQueue] addOperation:op];
</code></pre>
<p>然后网络请求如下所示:(使用Wireshark捕获)</p>
<pre><code>POST /upload?token=dd224bceb02929b36d35&agent=iPhone%20Simulator&v=1.0 HTTP/1.1
Host: localhost:6547
Transfer-Encoding: Chunked
Accept-Encoding: gzip, deflate
Content-Type: application/json; charset=UTF-8
Accept-Language: en-us
Connection: keep-alive
Accept: */*
User-Agent: MyApp/2.0 CFNetwork/672.0.8 Darwin/13.0.0
9BD
{"contacts": [(valid json array)]}
0
</code></pre>
<p>我不知道为什么9BD和0包含在请求正文中。我认为这是缓冲区设置的错误,我相信这会导致服务器忽略http主体,因为它是无效的。看起来我构建的请求正确吗?有更好的方法吗?我使用pyramid/python来处理请求。服务器接收请求正常,但请求正文为空。在</p>
<h2>编辑</h2>
<p>如果我不发送任何联系人,“9BD”就会消失。如果我更改联系人数据,“9BD”将更改为不同的字符。“0”总是在底部。在</p>
<h2>编辑2</h2>
<p>Jim指出请求的格式是有效的。这意味着服务器没有正确处理流。请求正确命中服务器,服务器响应正常。但是,我没有看到任何请求体。服务器正在运行pyramid/python。在服务器上,请求.正文是空的。在</p>