Выбор транспортов для JSON через TCP - PullRequest
16 голосов
/ 04 июля 2011

Я пишу простой потоковый сервис JSON. Он состоит из сообщений JSON, отправляемых с перерывами в течение длительного периода времени (недели или месяцы).

Каков наилучший способ отправки нескольких сообщений JSON через обычный сокет TCP?

Некоторые альтернативы, на которые я смотрел (и их недостатки):

  1. разделенный символом новой строки JSON - недостаток: для перехода на новую строку внутри JSON требуется экранирование или запрет
  2. Вдохновение 0x00 0xff в веб-сокете - недостаток: теперь это бинарный код, больше не utf-8
  3. реальные веб-сокеты - недостаток: отсутствие (с открытым исходным кодом) веб-сокета клиент библиотеки
  4. http multipart http://www.w3.org/Protocols/rfc1341/7_2_Multipart.html - недостаток: неполная поддержка клиента?
  5. без разделителей - обратная сторона: для разбиения на фрагменты требуется анализ JSON (нельзя просто посчитать curly из-за curly в строках)

Есть ли хороший или, по крайней мере, устоявшийся способ сделать это?

Ответы [ 5 ]

14 голосов
/ 04 июля 2011

мои первые два варианта будут:

  1. Делайте то, что делают ранние протоколы TCP: отправьте одно сообщение (в вашем случае объект JSON) и закройте соединение. Клиент обнаруживает его и открывает снова, чтобы получить следующий объект.

    • плюсы: очень легко разбирать, лишних (контентных) байтов не отправлено. любая потеря данных означает потерю только одного объекта. если вы можете выдержать это, нет необходимости добавлять ретрансмиссию в ваше приложение.
    • минусов: если вы отправляете (огромное) множество (очень) небольших объектов, трехпакетное TCP-квитирование увеличивает задержку.
  2. Сделайте то, что делает HTTP в режиме chunked: сначала отправьте число байтов в объекте JSON, символ новой строки (CRLF в HTTP) и ваш объект JSON. Клиенту просто нужно посчитать байты, чтобы узнать, когда следующий байт будет следующим размером объекта.

    • плюсы: вы держите один долгоживущий поток.
    • минусы: несколько дополнительных байтов, вам нужно сохранить долгоживущий поток, поэтому случайный разрыв и переподключение должны быть обработаны как исключительные события, необходимо установить некоторое подтверждение связи для продолжения в том месте, где он вышел из строя.
5 голосов
/ 14 августа 2011

Когда вы хотите обслуживать клиентов браузера, ближе всего к необработанному TCP вы получаете WebSockets.

У WebSockets есть достаточный импульс, чтобы поставщики браузеров улучшили поддержку (Chrome 14 и Firefox 7/8 поддерживают последние версии протокола) и что широкий спектр клиентских и серверных сред будет поддерживать его.

Там уже уже пара клиентских библиотек с открытым исходным кодом, включая Autobahn WebSocket .

Если вы хотите испечь что-то для себя (поверх чистого TCP), я бы порекомендовал формат с префиксом длины для ваших сообщений JSON, т.е. Netstrings

Отказ от ответственностиЯ - автор автобана и работаю на Тавендо.

1 голос
/ 19 апреля 2015

Вы можете использовать Отправленные сервером события .

var source = new EventSource('/EventSource');

source.onmessage = function(e) {
  var data = JSON.parse(e.data);
  console.log(e.data);
};

source.onopen = function(e) {
  console.log('EventSource opened');
};

source.onerror = function(e) {
  console.log('EventSource error');
};
1 голос
/ 02 июля 2013

Я систематизировал то, что делаю я и некоторые другие разработчики:

http://en.wikipedia.org/wiki/Line_Delimited_JSON

Преимущество совместимости с netcat / telnet.

Смотри также: http://ndjson.org/

0 голосов
/ 01 июля 2019

Первый из четырех байтов сообщения может быть 32-разрядным целым числом, указывающим размер (в байтах) сообщения. Затем получатель должен выполнить следующие действия:

  1. Считайте первые четыре байта данных и определите точное количество байтов, необходимое для чтения всего сообщения.
  2. Прочитать остальную часть сообщения и десериализовать его как JSON

Код отправителя в C #:

        public void WriteMessage(Packet packet) {
        // Convert the object to JSON
        byte[] message = Encoding.UTF8.GetBytes(packet.Serialize());

        // Serialize the number of characters
        byte[] messageLength = BitConverter.GetBytes(message.Length);

        // Build the full message that will hold both the size of the message and the message itself
        byte[] buffer = new byte[sizeof(int) + message.Length];

        Array.Clear(message, 0, message.Length);

        // Print the size into the buffer
        for (int i = 0; i < sizeof(int); i++)
        {
            buffer[i] = messageLength[i];
        }

        // Print the message into the buffer
        for (int i = 0; i < message.Length; i++)
        {
            buffer[i + sizeof(int)] = message[i];
        }

        // Send it
        stream.Write(buffer, 0, buffer.Length);
    }
...