C - Как определить количество байтов для сообщений JSON

Я работаю над проектом на базе Linux, состоящим из «основного» приложения, написанного на C, и веб-сервера, вероятно, написанного на Python. Главный сервер и веб-сервер должны иметь возможность связываться друг с другом через TCP / IP. Я сосредоточен на основном приложении на C.

Из-за разных языков программирования, используемых для ядра и веб-сервера, я ищу протокол сообщений, который легко использовать на обоих языках. В настоящее время я думаю, что JSON - хороший кандидат. Мой вопрос, однако, не столько в протоколе сообщений, сколько в том, как я могу определить количество байтов для чтения из (и, возможно, отправки) сокета, особенно при использовании протокола сообщений, такого как JSON или XML.

Насколько я понимаю, независимо от того, используете ли вы JSON, XML или какой-либо другой протокол сообщений, вы не можете включать размер сообщения в само сообщение, потому что для анализа сообщения вам потребуется все сообщение и, следовательно, нужно знать размер его заранее. Обратите внимание, что под «сообщением» я подразумеваю данные, отформатированные в соответствии с используемым протоколом сообщений.

Я думал и читал о решении этой проблемы и пришел к следующим двум возможностям:

  1. Определите максимально возможный размер сообщения, скажем 500 байтов, и на основе этого определите размер буфера, скажем 512 байтов, и добавьте заполнение к каждому сообщению, чтобы было отправлено 512 байтов;
  2. Добавьте к каждому сообщению его размер в виде «обычного текста». Если размер хранится в Int (4 байта), то получатель сначала считывает 4 байта из сокета и, используя эти 4 байта, определяет, сколько байтов нужно прочитать следующим для фактического сообщения;

Поскольку все предлагаемые решения, которые я прочитал, не были специально предназначены для использования какого-либо протокола сообщений, такого как JSON, я думаю, что, возможно, я что-то упускаю.

Итак, какая из двух предложенных мной возможностей является лучшей, или я не знаю другого решения этой проблемы?

С уважением.

Добро пожаловать в SO. Вы должны знать, что для TCP / IP нельзя полагаться на получение только одного сообщения. Вы также можете получить менее одного или более одного сообщения, поскольку соединение является потоком. Вы должны подготовиться к чтению части и дождаться получения оставшейся части.

Gerhardh 14.05.2018 10:55

На самом деле вам нужен протокол высокого уровня, чтобы иметь возможность собирать полное сообщение JSON. Но поскольку JSON ориентирован на текст, вы также можете использовать специальные байты, отличные от ascii, в качестве разделителей сообщений, потому что они не могут быть включены в сообщение JSON.

Serge Ballesta 14.05.2018 10:57

Или, скорее, не-Unicode, если вы позволяете это. Если вы собираетесь выбрать кодировку текста, вероятно, лучше всего использовать utf-8.

sudo 14.05.2018 10:58

@Gerhardh: Спасибо. Под сообщением я подразумеваю то, что я, как программист, определяю как сообщение. Скажем, размер этого сообщения составляет X байт. Тогда разве не правда, что вы читаете X байтов из сокета блокирующим образом, так что, когда вы заканчиваете чтение, вы читаете X байтов, что является сообщением?

Vincent 14.05.2018 10:59

@sudo: JSON кодирует байт не ASCII в ASCII, поэтому UTF8 не имеет значения.

Serge Ballesta 14.05.2018 11:00

@Vincent в потоках TCP нет корреляции между отдельными вызовами чтения из сокета и пакетами, отправленными по сети. Сам TCP использует это преимущество с помощью алгоритма Нэгла, который очень немного задерживает передачу очень маленьких пакетов в случае, если сразу после этого в очередь ставится другой пакет.

Alnitak 14.05.2018 11:01

@SergeBallesta неверно - JSON может нести UTF-8 изначально и не кодирует его специально.

Alnitak 14.05.2018 11:02

@Vincent Я очень рекомендую добавить значение длины сообщения. DNS через TCP делает это, используя двухбайтовое поле длины (отправляемое в двоичном, а не текстовом порядке, в прямом порядке) для максимального размера сообщения 65535 байт. Незначительным недостатком этого является то, что вы должны отображать свои данные в JSON (то есть в память), чтобы вы знали, сколько времени пройдет, прежде чем вы сможете начать их отправку.

Alnitak 14.05.2018 11:03

@sudo: Вы правы в общем случае. Модуль Python json кодирует символы, отличные от ascii, и json допускает такую ​​кодировку, но сообщение JSON может содержать любой не управляющий символ Unicode.

Serge Ballesta 14.05.2018 11:09

Я думаю, что строки @SergeBallesta Python 2 по умолчанию являются ASCII. Я забыл, как это работает, потому что я всегда использую 3, где строки - это Unicode, а модуль json кодирует символы Unicode.

sudo 14.05.2018 11:14
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
10
180
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это классическая проблема, с которой сталкиваются потоки, в том числе TCP, часто называемая «проблемой границы сообщения». Вы можете поискать более подробные ответы, чем то, что я могу дать здесь.

Чтобы определить границы, у вас есть несколько вариантов:

  • Фиксированная длина с заполнением, как вы сказали. Не рекомендуется, если у вас нет очень маленьких сообщений.
  • Подготовьте размер, как вы сказали. Если вы хотите создавать необычные сообщения и поддерживать большие сообщения, не тратя слишком много байтов, вы можете использовать количество переменной длины, где вы используете бит, чтобы определить, следует ли читать больше байтов для этого размера. @alnitak упомянул недостаток в комментариях, которые я не заметил, а именно то, что вы не можете начать отправку, пока не узнаете размер.
  • Связан с каким-то байтом, который вы больше нигде не используете (JSON и XML являются только текстовыми, поэтому '\ 0' работает с ASCII или любым UTF). Просто, но медленнее на принимающей стороне, потому что таким образом вы должны сканировать каждый байт.
  • Обновлено: JSON, XML и многие другие форматы также можно анализировать на лету для определения границ (например, каждый { должен быть закрыт с помощью } в JSON), но я не вижу в этом никаких преимуществ.

Если это не просто учебный процесс, вы можете вместо этого использовать существующий протокол, чтобы сделать все это за вас. Например, HTTP (неэффективный) или gRPC (более эффективный).

Изменения: изначально я сказал что-то совершенно неправильное о необходимости включать контрольную сумму для обработки потери пакетов, несмотря на TCP ... TCP не будет продвигаться, пока эти пакеты не будут должным образом получены, так что это не проблема. ИДК, о чем я думал.

Похоже, вы не знаете, как работает TCP. Вы не можете «потерять» такой пакет в TCP. Потерянный пакет будет обнаружен операционной системой и автоматически передан повторно.

Alnitak 14.05.2018 10:59

Моя беда, я полностью пропустил ошибку и забыл, что TCP не продвинется вперед без получения последнего сообщения. Редактирование ...

sudo 14.05.2018 11:00

@Alnitak В спецификации JSON говорится, что управляющие символы должны быть экранированы, если вы перейдете к tools.ietf.org/html/rfc7159 и выполните поиск «кроме символов, которые должны быть экранированы». Библиотеки C JSON, которые я видел, используют строки с завершающим нулем, как и многие библиотеки в C, и Python выдает ошибку, если вы пытаетесь декодировать строку, содержащую нулевые байты.

sudo 14.05.2018 19:18

Я пропустил бит на json.org, где написано «кроме управляющего символа»! В любом случае, в этом случае я бы все равно не стал использовать разделители.

Alnitak 15.05.2018 10:02

Другие вопросы по теме