Обработка потоков TCP
Наш сервер, по-видимому, основан на пакетах. Это адаптация от старой последовательной системы. Он был добавлен, изменен, перестроен и т. Д. За эти годы. Поскольку TCP является потоковым протоколом, а не пакетным протоколом, иногда пакеты разбиваются. ServerSocket разработан таким образом, что когда Клиент отправляет данные, часть данных содержит размер нашего сообщения, такой как 55
, Иногда эти пакеты делятся на несколько частей. Они поступают по порядку, но поскольку мы не знаем, как сообщения будут разделены, наш сервер иногда не знает, как определить разделенное сообщение.
Итак, предоставив вам справочную информацию. Каков наилучший метод для восстановления пакетов по мере их поступления, если они разделены? Мы используем C++ Builder 5 (да, я знаю, старая IDE, но это все, с чем мы можем работать в данный момент. МНОГО работы по редизайну в.NET или более новой технологии).
3 ответа
TCP
гарантирует, что данные будут доставлены в том же порядке, в котором они были отправлены.
При этом вы можете просто добавить все входящие данные в буфер. Затем проверьте, содержит ли ваш буфер один или несколько пакетов, и удалите их из буфера, сохраняя все оставшиеся данные в буфере для последующей проверки.
Это, конечно, предполагает, что ваши пакеты имеют некоторый заголовок, который указывает размер следующих данных.
Давайте рассмотрим пакеты, имеющие следующую структуру:
[LEN] X X X...
куда LEN
это размер данных, и каждый X является байтом.
Если вы получаете:
4 X X X
[--1--]
Пакет не завершен, вы можете оставить его в буфере. Затем поступают другие данные, вы просто добавляете их в буфер:
4 X X X X 3 X X X
[---2---]
Затем у вас есть 2 полных сообщения, которые вы можете легко проанализировать.
Если вы это сделаете, не забудьте отправить любую длину в независимой от хоста форме (ntohs
а также ntohl
может помочь).
Это часто достигается путем добавления к сообщениям префикса длиной в один или два байта, который, как вы сказали, дает длину оставшихся данных. Если я вас правильно понял, вы отправляете это в виде простого текста (то есть "5", "5"), и это может быть разделено. Поскольку вы не знаете длину десятичного числа, это несколько двусмысленно. Если вам абсолютно необходимо использовать обычный текст, возможно, вы могли бы закодировать длину как 16-битное шестнадцатеричное значение, то есть:
00ff <255 байтов данных> 000a <10 байтов данных>
Таким образом, длина заголовка размера фиксируется на 4 байта и может использоваться как минимальная длина чтения при получении на сокете.
Редактировать: Возможно, я неправильно понял - если чтение значения длины не является проблемой, решите проблему разделения, объединяя входящие данные в строку, байтовый буфер или что угодно, пока его длина не станет равной значению, которое вы прочитали в начале. TCP позаботится обо всем остальном.
Примите дополнительные меры предосторожности, чтобы убедиться, что вы не можете застрять в состоянии блокирующего чтения, если клиент не отправит полное сообщение. Например, скажем, вы получили заголовок длины и запускаете цикл, который продолжает считывать блокировку вызовов recv() до тех пор, пока буфер не будет заполнен. Если злонамеренный клиент намеренно прекращает отправку данных, ваш сервер может быть заблокирован до тех пор, пока клиент не отключится или не начнет отправку.
Я хотел бы иметь функцию readBytes или что-то, что принимает буфер и параметр длины и читает, пока не будет прочитано столько байтов. Вам нужно будет зафиксировать количество фактически прочитанных байтов, и, если оно меньше ожидаемого, увеличьте указатель буфера и прочитайте остальные. Продолжайте, пока вы не прочитаете их все.
Затем вызовите эту функцию один раз для заголовка (содержащего длину), предполагая, что заголовок имеет фиксированную длину. Получив длину фактических данных, снова вызовите эту функцию.