【问题标题】:Data gets truncated when sending over NSStream通过 NSStream 发送时数据被截断
【发布时间】:2012-07-11 00:21:18
【问题描述】:

好的,所以我整天都在处理这个错误,我想我已经把它缩小到了根本问题。

背景:

我正在开发一个应用程序,该应用程序要求我编写自己的 NSNetServiceNSNetServiceBrowser 版本,以便在 iOS 5 中通过蓝牙支持 Bonjour。这是一次伟大的冒险,因为我对网络编程一无所知在我开始这个项目之前。我从各种示例项目和经典的Unix Network Programming 教科书中学到了很多东西。我的实现主要基于 Apple 的 DNSSDObjects 示例项目。一旦解决了服务,我添加了代码以实际建立设备之间的连接。 NSInputStreamNSOutputStream 通过 CFStreamCreatePairWithSocketToHost( ... ) 获得。

问题:

我正在尝试通过此连接发送一些数据。数据由一个整数、几个NSStrings 和一个NSData 对象组成,该对象用NSKeyedArchiver 归档。 NSData 的大小约为 150kb,因此整个消息的大小约为 160kb。通过连接发送数据后,当我尝试取消归档时出现以下异常...

Terminating app due to uncaught exception 'NSInvalidArgumentException', 
reason: '*** -[NSKeyedUnarchiver initForReadingWithData:]: incomprehensible archive 

经过进一步探索,我注意到接收到的数据只有 2kb 左右。消息正在被截断,从而使存档“难以理解”。

可能相关的代码:

向所有连接设备发送数据的方法

- (void) sendMessageToPeers:(MyMessage *)msg
{
  NSEnumerator *e = [self.peers objectEnumerator];

  //MyMessage conforms to NSCoding, messageAsData getter calls encodeWithCoder:
  NSData *data = msg.messageAsData; 

  Peer *peer;
  while (peer = [e nextObject]) {
    if (![peer sendData:data]) {
      NSLog(@"Could not send data to peer..");
    }
  }
}

Peer类中实际向NSOutputStream写入数据的方法

- (BOOL) sendData:(NSData *)data
{
  if (self.outputStream.hasSpaceAvailable) {
    [self.outputStream write:data.bytes maxLength:data.length];
    return YES;
  }
  else {
    NSLog(@"PEER DIDN'T HAVE SPACE!!!");
    return NO;
  }
}

用于处理流事件(“接收”数据)的 NSStreamDelegate 方法

此代码中的缓冲区大小是 32768 b/c,这就是我从中学到的任何示例代码中的大小。它是任意的吗?我尝试将其更改为 200000,认为问题只是缓冲区太小,但它没有改变任何东西.. 我不认为我完全理解发生了什么。

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
  switch (eventCode) {

    case NSStreamEventHasBytesAvailable: {
      NSInteger       bytesRead;
      uint8_t         buffer[32768];      // is this the issue?
//    uint8_t         buffer[200000];   //this didn't change anything

      bytesRead = [self.inputStream read:buffer maxLength:sizeof(buffer)];
      if (bytesRead == -1) ...;
      else if (bytesRead == 0) ...;
      else {
        NSData  *data = [NSData dataWithBytes:buffer length:bytesRead];
        [self didReceiveData:data];
      }
    } break;

    /*omitted code for other events*/
  }
}

【问题讨论】:

  • 添加逻辑以实际计算您发送和接收的字节数。我怀疑您只收到第一个缓冲区。您的 didReceiveData 方法应该是从多个缓冲区累积数据。

标签: objective-c ios5 network-programming nsstream nscoder


【解决方案1】:

NSStream 通过这样的网络将使用 TCP 连接。它可能会有所不同,但最大数据包大小通常在 2k 左右。由于您发送的消息实际上是 160k,因此它将被拆分为多个数据包。

TCP 将其抽象为一个数据流,因此您可以确定所有这些数据包都会以正确的顺序接收。

但是,stream:handleEvent: 委托方法可能在只有前 2k 数据到达时被调用——它无法知道还有更多数据到达。

请注意,read:maxLength: 方法并不保证你总是能得到那个最大长度——在这种情况下,它似乎只给你最多 2k。

您应该计算实际的bytesReceived,并将所有数据连接在一起,直到收到您等待的总金额。

接收方如何知道它需要多少数据? – 您可能希望设计您的协议,因此在发送数据之前,您发送一个定义大小的整数,指示即将到来的数据的长度。或者,如果您只通过套接字发送一条消息,您可以在完成后简单地关闭它,并在套接字关闭后让接收方取消归档。

【讨论】:

  • 我认为您对此是正确的 :) 我会实现它并看看它是如何进行的
【解决方案2】:

您似乎正在从 self.inputStream 中读取,但传入您的 stream:handleEvent: 方法的流称为 aStream。他们是否以某种方式引用同一个对象?否则我不确定您是否正在读取实际上有可用字节的流

【讨论】:

  • 好点.. 我不知道为什么我这样写。它们确实引用了同一个对象,并且该类只是一个 NSInputStream 的 NSStreamDelegate
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-12
  • 2021-07-15
  • 1970-01-01
  • 2011-12-19
相关资源
最近更新 更多