5

在我的基于 TCP 套接字的服务器上,我通过流发送一个数据包,其中数据包由一个标头组成,该标头指定数据包中的字节数,然后是该字节数。对于那些熟悉 Erlang 的人,我只是设置了 {packet, 4} 选项。在 iOS 端,我的代码看起来像这样,假设我想弄清楚这条消息的流的大小:

[asyncSocket readDataToLength:4 withTimeout:-1 tag:HEADER_TAG];

这工作正常,并调用以下委托方法回调:

onSocket:didReadData:withTag:

我认为下一个合乎逻辑的步骤是计算流的大小,我这样做:

  UInt32 readLength;
  [data getBytes:&readLength length:4];
  readLength = ntohl(readLength);

在服务器端硬编码一个 12 字节的字符串后,readLength 确实在客户端也读取了 12,所以到目前为止一切都很好。我继续进行以下操作:

 [sock readDataToLength:readLength withTimeout:1 tag:MESSAGE_TAG];

此时虽然onSocket:didReadData:withTag:不再调用回调。相反,读取超时正在发生,可能是因为我没有正确处理读取,这个委托方法被调用:

- (NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutReadWithTag:(long)tag elapsed:(NSTimeInterval)elapsed bytesDone:(NSUInteger)length 

因此,服务器总共发送 16 个字节、一个 4 个字节的标头和一个 12 个字节的二进制流。

我确信错误在于我如何使用 CocoaAsyncSocket。在我弄清楚它的大小之后,阅读流的其余部分的正确方法是什么?

** 更新 **

我改变了我的客户,它现在似乎正在工作。问题是,我不明白 readDataToLength 与新解决方案的意义。这是我将最初的阅读更改为:

[socket readDataWithTimeout:-1 tag:HEADER_TAG];

现在在我的回调中,我只需执行以下操作:

- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
    if (tag == HEADER_TAG) {
        UInt32 readLength;
        [data getBytes:&readLength length:4];
        readLength = ntohl(readLength);
        int offset = 4;
        NSRange range = NSMakeRange(offset, readLength);
        char buffer[readLength];
        [data getBytes:&buffer range:range];
        NSLog(@"buffer %s", buffer);
        //[sock readDataToLength:readLength withTimeout:1 tag:MESSAGE_TAG];
    } else if (tag == MESSAGE_TAG) {
        //[sock readDataToLength:4 withTimeout:1 tag:HEADER_TAG];
    }

}

所以一切都以一个原子有效载荷的形式出现。也许这是因为 Erlang {packet, 4} 的工作方式。我希望是的。否则,readDataToLength 的意义何在?没有办法在客户端提前知道消息的长度,那么在什么情况下可以使用该方法呢?

4

1 回答 1

1

我想这取决于你如何从 Erlang 端发送。该选项{packet, 4}将发送每个带有 4 字节长度前缀的数据包。Erlang 中的每个发送操作都将导致发送一个带有长度前缀的数据包(4例如 length 的最大大小为 2 Gb)。Erlang 文档的相关部分是使用inet:setopts/2.

我猜数据是到目前为止从套接字读取的总累积数据。如果该数据包含您的整个数据包,那很好。但如果没有,您可能希望继续使用readDataToLength剩余数据从套接字进行阻塞读取。

于 2011-07-22T07:42:57.933 回答