Ruby пакетная буферизация и разбиение - PullRequest
1 голос
/ 03 июля 2011

Итак, я думаю, что это немного широко, но я постараюсь сузить это настолько, насколько смогу. У меня есть сервер (с EventMachine), и иногда пакеты делятся, но иногда они буферизируются. Итак, я попытался создать функцию, которая будет буферизовать / снять с них буфер. Мне удалось что-то сделать, однако это не работает «как ожидалось». Честно говоря, я сомневаюсь, что могу даже назвать это «едва функциональным».

Прежде всего я хотел бы указать на структуру пакета:

  • Первые четыре байта пакета - это его идентификатор или имя пакета (name).
  • Следующие четыре байта являются длиной части пакета «msg» (len).
  • И последние четыре байта перед частью сообщения - это поле ссылки, которое имеет различное использование (ref).

Примечание: lenf - это необработанный формат len, поэтому я думаю, что строка не так важна.


Код буфера

def split(data)
    if ($packet_buffer != "" && !$packet_buffer.nil?)
        data = $packet_buffer + data
        $packet_buffer = ""
    end
    last = 0
    packets = []
    loop do
        if data[last..-1].length < 8
            $packet_buffer = data[last..-1]
            break
        end
        name = data[last...last+=4]
        lenf = data[last...last+4]

        len = 0
        data[last...last+=4].each_byte {|b| len+=b}

        if !data[last+4..-1].nil? && data[last+4..-1].length < len
            $packet_buffer = data
            break
        end

        ref = data[last...last+=4]
        msg = data[last...last+=len]

        packets << (name << lenf << ref << msg)

        break if data[last..-1].nil?
    end
    packets
end

TLDR

Как разделить буферизованный и разделить буфер пакетов / данных (переданных EventMachine) в Ruby?

Обновление: Пакеты отправляются по TCP. Данные поступают от клиента, созданного на C, так что да, это поток байтов.

Я не уверен, что именно не так, но метод, похоже, не разделяет и не буферизует пакеты должным образом. Он отлично работает, пока получает небольшие объемы данных (которые, я полагаю, не буферизируются и не разделяются).

Иногда он даже успешно разделяет пакеты, если они буферизируются, но, похоже, буферизация не работает вообще


Я вполне уверен, что я испортил некоторую часть "логики" здесь, однако я просто не могу понять, что это такое. Любая помощь будет принята с благодарностью.

Спасибо

Ответы [ 2 ]

1 голос
/ 03 июля 2011

Хорошо, вот одна ошибка, которая выскакивает у меня:

len = 0
data[last...last+=4].each_byte {|b| len+=b}

Вы не указали, в каком формате вы сохраняете длину, но если это немного порядковое число, то вы должны сделать что-то вроде len = (len>>8) + (b<<24) вместо того, чтобы просто сложить все байты вместе, как вы делаете сейчас.Ваш текущий алгоритм будет работать нормально, если len всегда будет меньше 256.

Здесь могут быть другие логические ошибки.Мне не нравится, когда вы используете запутанные выражения типа data[last..-1].nil?;Я бы переписал их как простые неравенства, включающие data.length и last.

Если вы хотите действительно очистить свой код, то я бы рекомендовал использовать другой подход: передать байты в новую функцию с именем process_byte по одному.Эта функция будет отвечать за отслеживание любой необходимой ей информации о состоянии (например, какую часть сообщения она ожидает получить следующей), сборку байтов в полные сообщения и передачу сообщения участника в код более высокого уровня.Функция process_byte не будет знать о том, как были упакованы байты, поэтому сразу вы можете устранить определенный класс ошибок, которые может иметь ваша программа.

Вы можете использовать волокна Ruby для реализации функции process_byte вхороший способ, позволяющий писать код, который выглядит синхронно (например, len += get_next_byte()), но на самом деле будет асинхронным.

0 голосов
/ 03 июля 2011

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

def split(data)
    packets = []
    loop do
        if !$packet_buffer.nil?
            data = $packet_buffer << data
            $packet_buffer = nil
        end

        if data.length < 8
            $packet_buffer = data
            break
        end


        len = calc_Uint32(data[4...8])

        if data.length-12 < len
            $packet_buffer = data
            break
        end

        packets << data[0...12+len]
        data[0...12+len] = ''

        break if data.length == 0
    end
    packets
end #split

Я искренне сомневаюсь, что кто-то найдет это полезным, поскольку он не настолько универсален, но я надеюсь, что в конце концов кто-то найдет для него применение.

...