读取从JavaEE服务器返回的字节流

时间:2008-11-27 08:39:33

标签: iphone objective-c cocoa-touch

我们有一个JavaEE服务器和servlet为移动客户端提供数据(第一个JavaME,现在很快就是iPhone)。 servlet使用以下代码写出数据:

DataOutputStream dos = new DataOutputStream(out);

dos.writeInt(someInt);

dos.writeUTF(someString);

......等等

此数据作为HTTP响应正文中的字节返回给客户端,以减少传输的字节数。

在iPhone应用程序中,响应有效负载被加载到NSData对象中。现在,花了几个小时试图弄清楚如何在Objective-C应用程序中读取数据,我几乎已经准备好放弃了,因为我还没有找到任何好的方法将数据读入NSInteger和NSString (与上述协议相对应)

有没有人有任何指针如何从java应用程序编写的二进制协议中读取内容?非常感谢任何帮助!

谢谢!

4 个答案:

答案 0 :(得分:2)

你必须自己做脱毛;幸运的是,它相当简单。 Java的DataOutputStream类以big-endian(网络)格式写入整数。因此,为了对整数进行解组,我们获取4个字节并将它们解压缩为4字节整数。

对于UTF-8字符串,DataOutputStream首先写入一个2字节的值,表示后面的字节数。我们在中读取,然后读取后续字节。然后,要解码字符串,我们可以使用NSString方法initWithBytes:length:encoding:

NSData *data = ...;  // this comes from the HTTP request
int length = [data length];
const uint8_t *bytes = (const uint8_t *)[data bytes];

if(length < 4)
    ; // oops, handle error

// demarshall the big-endian integer from 4 bytes
uint32_t myInt = (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | (bytes[3]);
// convert from (n)etwork endianness to (h)ost endianness (may be a no-op)
// ntohl is defined in <arpa/inet.h>
myInt = ntohl(myInt);

// advance to next datum
bytes += 4;
length -= 4;

// demarshall the string length
if(length < 2)
    ; // oops, handle error
uint16_t myStringLen = (bytes[0] << 8) | (bytes[1]);
// convert from network to host endianness
myStringLen = ntohs(myStringLen);
bytes += 2;
length -= 2;

// make sure we actually have as much data as we say we have
if(myStringLen > length)
    myStringLen = (uint16_t)length;

// demarshall the string
NSString *myString = [[NSString alloc] initWithBytes:bytes length:myStringLen encoding:NSUTF8StringEncoding];
bytes += myStringLen;
length -= myStringLen;

您可以(也可能应该)将函数写入demarshall,这样您就不必为要解映的每个字段重复此代码。另外,要特别小心关于缓冲区溢出。您正在处理通过网络发送的数据,您应始终不信任该数据。 始终验证您的数据,始终检查您的缓冲区长度。

答案 1 :(得分:1)

最重要的是要了解二进制数据格式本身。无论写什么都没关系,只要你知道字节是什么意思。

因此,DataOutputStream的文档是您最好的选择。它们指明了二进制数据的所有内容(希望如此)。

接下来,我会尝试基本上在iPhone上提供一个类,它将相同的格式读入适当的数据结构。我根本不知道Objective C,但是我确信读取4个字节并不难,知道第一个字节是最重要的(等等)并做适当的bit-twiddling以获得正确的一种整数。 (基本上读取一个字节,将其向左移动8,读取下一个字节并将其添加到结果中,将整个批次移位8位等等)可能有更有效的方法来做到这一点,但得到的东西先行。当你对它进行单元测试时,你可以进行优化。

答案 2 :(得分:0)

不要忘记Objective-C只是穿着漂亮衣服的C而且C擅长于这种有点卑躬屈膝的感觉。在很大程度上,您应该能够只定义一个看起来像您的数据的C结构,并将指向数据的指针转换为指向该结构的指针。现在,确切地使用哪些类型,以及是否需要进行字节交换,将取决于Java如何构造此流;这就是你需要花时间使用Java的文档。

但从根本上说,这是一种设计气味。您遇到此问题是因为您对客户端平台的假设不再有效。如果它是一个选项,我建议你为相同的功能提供第二个,更便携的接口(只需添加“WithXML”包装或某些东西就足够了)。如果你最终移植到另一个不使用Java的平台,这将节省你的时间。

答案 3 :(得分:0)

Carl,如果你真的无法改变服务器提供的内容,请查看this class。它应该是您正在寻找的指针。这就是说:使用原生java序列化作为传输格式的想法听起来不是一个好主意。我的第一选择是JSON。如果那仍然太大我可能宁愿使用像ThriftProtobuffers这样的东西。它们还提供二进制序列化 - 但是以交叉语言的方式。 (还有老人ASN1 - 但这很痛苦)