Подтвердить что ты не робот

Выбор транспорта для JSON через TCP

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

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

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

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

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

4b9b3361

Ответ 1

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

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

Это имеет преимущество, заключающееся в совместимости с netcat/telnet.

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

Ответ 2

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

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

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

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

Ответ 3

Если вы хотите обслуживать клиентов браузера, наиболее близким к необработанному TCP является WebSockets.

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

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

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

Отказ от ответственности: я автор Autobahn и работаю в Tavendo.

Ответ 4

Вы можете использовать Server-Sent Events.

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');
};

Ответ 5

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

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

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

        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);
    }