如何从异步tcp套接字读取可变长度数据?

时间:2014-01-02 23:51:37

标签: objective-c protocol-buffers cocoaasyncsocket

我正在将CocoaAsyncSocket用于iOS项目。我试图通过异步接口读取VarInts。问题不像String这样的东西,我可以在前面加上长度,我不知道varint的长度。它需要一次处理一个字节,但由于每个读取操作都是异步的,因此其他读取调用可能已在其间排队。

我考虑读入缓冲区然后处理它,比如读取5个字节(varint-32的最大长度),然后推回额外的字节,但如果varint只有4个字节而且我可能会不必要地挂起等待第5个字节可用。

我该怎么做?另外,我不能改变另一端的协议,使用固定大小的整数。


这是Josh要求的一段代码

- (void)readByte:(void (^)(int8_t))onComplete {
    NSUInteger size = 1;
    int32_t tag = OSAtomicAdd32(1, &_nextTag);
    dispatch_async(self.dispatchQueue, ^{
        [self.onCompleteHandlers setObject:(^void (NSData* data) {
            int8_t x = 0;
            [data getBytes:&x length:size];
            onComplete(x);
        }) forKey:[NSNumber numberWithInteger:((NSInteger) tag)]];
        [self.socket readDataToLength:size withTimeout:-1 tag:tag];
    });
}

回调保存在字典中,该字典在委托方法socket: didReadData: withTag中使用。

假设我正逐字节地读取VarInt:

  • 执行varint的第一个字节读取
  • 不知道我们是否需要为varint读取另一个字节;这取决于第一次阅读的结果
  • (可能)读取另一个字节
  • 读取varint的第二个字节,但现在它实际上是正在读取的第3个字节

我可以想象使用一个标志来指示我是否处于多部分读取,以及一个队列来保存应该在多部分读取之后执行的读取,并且我已经开始编写它但是它非常混乱。只是想知道是否有标准/推荐/更好的方法来解决这个问题。

1 个答案:

答案 0 :(得分:1)

简而言之,有4种方法可以知道从套接字中读取多少...

  1. 阅读一些可以推断出长度的格式,例如Content-Length标题...仅在整个请求可以在发送正文之前放在一起时才有效。
  2. 读取一些模式:如标题末尾的\r\n\r\n
  3. 读取直到某个超时...在n秒之后没有字节后,刷新缓冲区并关闭连接。
  4. 读取,直到服务器关闭连接...实际上曾经很常见。
  5. 这些都有问题,我可能会在你的情况下使用现有的协议。

    当然这样做有开销,你可能会发现你不想使用任何应用程序级别的东西,你的请求可能是这样的:

    client>"doMath(2+5)\0"
    
    server>"(7)\0"
    

    但很难具体回答你的一般性问题。

    修改

    所以我对varint base-128问题进行了一点研究,我认为实际上只有超时或关闭连接的服务器才能正常工作,如果你在TCP级写这些是非常糟糕的......