Как я должен пометить конец TCP-пакета?
В клиентском/серверном приложении текстовые данные разной длины будут отправляться туда и обратно между клиентом и сервером, как я должен отмечать конец отправляемого пакета? Например, когда сервер принимает пакетные данные от клиента, как сервер знает, что пакет клиента полностью получен?
Является ли более распространенным рассказать серверу о полной длине пакета, который он собирается получить перед данными, или иметь что-то, обозначающее конец пакета?
Некоторые из отправленных данных будут содержать только несколько символов, а некоторые могут быть тысячами символов.
Ответы
Ответ 1
TCP обеспечивает непрерывный поток данных. TCP реализуется с использованием пакетов, но вся точка TCP заключается в их скрытии.
Подумайте об этом, как будто это стена, на которой вы хотите рисовать. Стена сделана из кирпича. Кирпичи склеены вместе с раствором, и к нему прикладывается штукатурка, которая становится гладкой. Кирпичи - это IP-пакеты, TCP - штукатурка.
Итак, теперь у вас есть гладкий оштукатуренный туннель TCP, и вы хотите добавить в него какую-то структуру. Вы хотите рисовать коробки, чтобы ваши рисунки сохранялись отдельно друг от друга. Это то, что вы хотите сделать: добавить немного "административной" структуры (коробки вокруг чертежей) к вашим данным.
Многие протоколы используют концепцию packet
, которая представляет собой совокупность данных, начинающихся с административного заголовка фиксированного формата. Заголовок содержит достаточно информации, чтобы решить, где заканчивается пакет; например, он включает в себя длину пакета. HTTP делает это с заголовком Content-Length
или (с HTTP/1.1) с "закодированной кодировкой передачи", где данные разбиваются на один или несколько мини-пакетов, каждый из которых имеет простой заголовок, состоящий из точно мини-пакет- длина.
Другой способ - иметь специальную последовательность терминатора, которая не может появляться в "нормальных данных". Если ваши данные являются текстом, вы можете использовать байт нулевого значения в качестве терминатора.
Еще один способ - использовать самозавернувшиеся данные. Это структурированные данные таким образом, что вы можете в любой момент узнать, достигнут ли конец элемента. Например, данные XML организованы в виде вложенных пар маркеров, таких как <foo>...</foo>
. Когда конечный маркер (</foo>
) достигнут, вы знаете, что элемент закончен.
Ответ 2
Создайте свой пакет таким образом, чтобы он включал поле длины в начале.
Ответ 3
Если отправитель знает длину, то отправитель должен указать длину спереди как поле фиксированного размера, за которым следуют данные с переменным размером.
Преимущество против маркера хвоста заключается в том, что получатель может оптимизировать ожидаемый объем данных, например. выделите буфер нужного размера. Например, хранение по протоколам TCP/IP имеет ту же проблему по TCP/IP, что и вы. В этих случаях заголовки предоставляют длину ожидаемых впоследствии данных.
Позже по дороге вы можете найти другие бит, чтобы поместить в свой "заголовок". Вы будете рады, что у вас есть какая-то структура для создания собственного протокола уровня 5.
Ответ 4
Возьмите свои реплики из HTTP.
Используйте последовательность терминаторов символов или укажите длину где-нибудь в заголовке сообщения или используйте умную комбинацию обоих.
Как и HTTP: заголовки заканчиваются CR-LF-CR-LF. Если в заголовках есть данные, длина данных находится в одном из заголовков.
Ответ 5
Если вы чувствуете особую смелость, вы можете использовать сокеты SCTP вместо сокетов TCP.
Ответ 6
Остерегайтесь мусора, если вы кодируете длину в начале. Например, если вы используете 4 двоичных байта для длины, а какой-то внешний зонд отправляет HTTP-запрос, вы, скорее всего, окажетесь в огромном количестве и будете ждать навсегда (не говоря уже о распределении буфера, который может привести к сбою вашей программы). Я отправляю длину два раза каждый с помощью другой функции и сравниваю их (например, ~ len и len xor 0x139AF321). Вы также должны установить максимум, если кто-то активно пытается сбой вашей программы. Если я получаю плохую длину, я просто закрываю соединение.
Это больше, чем HMAC, если ваш трафик зашифрован.