Чтение потока байтов, возвращенного с сервера JavaEE - PullRequest
1 голос
/ 27 ноября 2008

У нас есть сервер JavaEE и сервлеты, предоставляющие данные мобильным клиентам (сначала JavaME, а теперь и iPhone). Сервлет записывает данные, используя следующий код:

DataOutputStream dos = new DataOutputStream(out);

dos.writeInt(someInt);

dos.writeUTF(someString);

... и т. Д.

Эти данные возвращаются клиенту в виде байтов в теле ответа HTTP, чтобы уменьшить количество передаваемых байтов.

В приложении iPhone полезная нагрузка ответа загружается в объект NSData. Теперь, потратив много часов на то, чтобы понять, как считывать данные в приложении Objective C, я почти готов сдаться, так как я не нашел хорошего способа считывания данных в NSInteger и NSString. (в соответствии с вышеуказанным протоколом)

У кого-нибудь есть указания, как считывать данные из двоичного протокола, написанного Java-приложением? Любая помощь с благодарностью!

Спасибо!

Ответы [ 4 ]

2 голосов
/ 29 мая 2009

Вы должны будете сделать демаршаллинг самостоятельно; к счастью, это довольно просто. Класс Java DataOutputStream записывает целые числа в формате с прямым порядком байтов (сетевым). Итак, чтобы демаршировать целое число, мы берем 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, чтобы вам не приходилось повторять этот код для каждого поля, которое вы хотите demarshall. Кроме того, будьте особенно осторожны относительно переполнения буфера . Вы обрабатываете данные, отправленные по сети, которые вы всегда должны не доверять. Всегда проверяет ваши данные, а всегда проверяет длину вашего буфера.

1 голос
/ 27 ноября 2008

Главное - понять сам формат двоичных данных. Неважно, что написано, если вы знаете, что означают байты.

Таким образом, документы для DataOutputStream являются вашей лучшей ставкой. Они определяют все (надеюсь), как будут выглядеть двоичные данные.

Далее, я бы попытался создать класс для iPhone, который будет считывать тот же формат в соответствующую структуру данных. Я вообще не знаю Objective C, но я уверен, что не может быть слишком сложно прочитать 4 байта, знаю, что первый байт является наиболее значимым (и т. Д.), И сделаю соответствующие бит-тиддлинг, чтобы получить правильный вид целого числа. (В основном читайте байт, сдвигайте его влево на 8, читайте следующий байт и добавляйте его в результат, сдвигайте весь лот влево на 8 бит и т. Д.) Возможно, есть более эффективные способы сделать это, но сначала получите что-то, что работает , Когда у вас есть модульные тесты, вы можете приступить к их оптимизации.

0 голосов
/ 02 декабря 2008

Карл, если вы действительно не можете изменить то, что предоставляет сервер, посмотрите на этот класс . Это должен быть указатель, который вы ищете. Тем не менее, идея использовать нативную сериализацию Java в качестве транспортного формата не выглядит хорошей идеей. Мой первый выбор был бы JSON . Если он все еще слишком велик, я бы лучше использовал что-то вроде Thrift или Protobuffers Они также обеспечивают двоичную сериализацию - но на нескольких языках. (Есть также старый ASN1 - но это больно)

0 голосов
/ 27 ноября 2008

Не забывайте, что Objective-C - это просто C в симпатичном платье - и C отлично справляется с подобным унижением. В значительной степени вы должны иметь возможность просто определить структуру C, которая выглядит как ваши данные, и преобразовать указатель на ваши данные в указатель на эту структуру. Теперь, какие именно типы использовать, и если вам нужно что-то поменять байтами, будет зависеть от того, как Java создает этот поток; для этого вам нужно потратить время на документирование Java.

По сути, это дизайнерский запах. У вас возникла эта проблема, потому что вы сделали предположения о своей клиентской платформе, которые больше не действительны. Если это вариант, я бы порекомендовал вам предложить второй, более переносимый интерфейс для тех же функций (достаточно просто добавить оболочки «WithXML» или что-то еще). Это сэкономит ваше время, если вы когда-нибудь будете портировать на другую платформу, которая не использует Java.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...