Длина пакета iOS
Я пишу небольшое приложение, которое по сути меняет местами XML туда-сюда в стиле SOAP. У меня есть сервер на базе OS X и клиент для iPad. я использую KissXML
на клиенте и встроенный парсер XML на сервере. я использую GCDAsyncSocket
на обоих общаться.
Когда я тестирую свое приложение на симуляторе iPad, получается полный XML. Все отлично работает
Тем не менее, когда я использую свое устройство разработки (настоящий физический iPad), все остальное работает нормально, но XML заканчивается после 1426-го символа. Я проверил, что эта ошибка происходит на нескольких iPad.
Когда я подписываюсь на входящие пакеты на GCDAsyncSocket
я использую [sock readDataWithTimeout:-1
buffer:[NSMutableData new]
bufferOffset:0
maxLength:0
tag:0];
а ранее просто простое [sock readDataWithTimeout:-1 tag:0];
но оба имеют одинаковый результат. Кажется, что GCDAsyncSocket не виноват во всяком случае, так как выполнение на симуляторе в порядке. Обратите внимание, что 0
в maxLength
указывает на "бесконечный" буфер.
Кто-нибудь знает, что может быть причиной этого?
3 ответа
1426 звучит очень похоже на MTU (Maximum Transmit Unit), который является максимальным размером данных TCP, которые вы можете отправить. Это разные размеры на разных сетевых носителях и разных конфигурациях, но 1426 довольно распространено.
Это говорит о том, что вы путаете прием TCP-пакета с завершением XML-сообщения. Нет гарантии, что TCP-пакеты будут заканчиваться на границе сообщения XML. GCDAsyncSocket - это низкоуровневая библиотека, которая поддерживает протокол TCP, а не XML.
Когда вы получаете каждый пакет, вы обязаны объединить его в NSMutableData
а затем решить, когда у вас будет достаточно, чтобы обработать его. Если ваш протокол закрывает соединение после каждого сообщения, вы можете читать, пока соединение не будет закрыто. Если нет, то вам придется иметь дело с тем фактом, что данный пакет может даже включать часть следующего сообщения. Вам придется анализировать данные в достаточной степени, чтобы решить, где находятся границы.
Кстати, вполне возможно, что ваш Mac имеет другой MTU, чем ваш iPad, поэтому вы можете наблюдать различное поведение на разных платформах.
Решение состояло в том, что когда не указано, AsyncSocket
смотрит на следующую строку-возврат. Когда пакет завершается, он действительно возвращает строку. Я использовала (sock
мой объект GCDAsyncSocket)
[sock readDatawithTimeout:-1 tag:0]
но с тех пор переехал в
[sock readDataToData:[msgTerm dataUsingEncoding:NSUTF8StringEncoding]
withTimeout:-1
tag:0]
где msgTerm
является внешней константой NSString, определенной как "\r\n\r\n", и является общей для клиентского и серверного источника. Это эффективно обходит проблему возврата строки, заканчивающую пакет.
Еще одно примечание, касающееся этого решения: поскольку я использую протокол, подобный SOAP, пробелы не являются проблемой. Однако, если у вас есть темперамент по поводу завершения пробельных строк, вы можете использовать такой метод, как [incomingDecodedNsstringMessage stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]
убрать это.
Посмотрев на код для GCDAsyncSocket
Я бы сказал, что вполне возможно, что в этом есть ошибка. Например, если вы читаете защищенный сокет, на iPhone используется механизм cfsocket вместо обычных файловых дескрипторов в стиле Unix, и автор может делать неверные предположения о том, когда сокет закрыт. Поскольку у вас есть исходный код, я бы попробовал пройтись по нему с помощью отладчика, чтобы посмотреть, не был ли конец файла помечен преждевременно.
TCP - это потоковый протокол. Теоретически размер пакета базового IP-протокола не должен иметь значения, но если вы будете читать сокет достаточно быстро, вы вполне можете получить данные в виде кусков размера IP-пакета, особенно если стек IP-адресов каким-то образом настроен для использования памяти (угадай здесь!).