LINUX.ORG.RU

Подскажите по алгоритмам


0

0

Имеется задача - работать с двунаправленным сокетом.

Т.е. я туда отправляю какие-нить пакеты и получают ответы в виде таких же пакетов. Формат пакетов простой (что-то вроде http). Роутинг пакетов оставим за кадром, это не проблема.

Проблема в, казалось бы, тривиальной штуке - как мне разбирать входящие данные?

Если я открываю сокет в синхронном режиме, то попытка чтения некого количества байт (N), вызов recv(N) может повиснуть на неопределенное время. Делать recv() даже по одному байту - не спасение, да и паразитная нагрузка.

В асинхронном режиме я поллю сокет и читаю ровно столько, сколько там накопилось. Дальше проблема выпарсивать пакет. Пакет мог прийти не полностью или могло прийти сразу несколько пакетов. До сих пор я все делал регекспами или каким-нить split, если формат пакета позволял, но подозреваю, что есть более кошерные, красивые способы. Итоговый код выглядит совершенно уродливо и меня терзают подозрения, что есть красивые, давно придуманные до меня, способы.

Помогите с ключевыми словами для поиска.

★★★★★

Вместо recv можно делать select/poll, а затем читать столько байтов, сколько есть у сокета (см. ioctl FIONREAD).

Парсить пакеты можно парсерами. Например, задать грамматику на yacc, и задать нужные действия на приход пакета.

dmitry_vk ★★★
()

Если я правильно тебя понял подойдет следущее: В асинхронном режиме пиши данные в достаточно большой буфер. Алгоритм: ищешь в буфере пакет, нашел — забираешь и «сдвигаешь буфер» влево на длину пакета, ненашел - ставишь на пуллинг. И так в цикле пока не найдешь в буфере пакет.

shelA
()

Обычно вышестоящий над TCP протокол либо передает длину пакета, сколько читать (в HTTP это Content-Length), либо просто закрывает соединение после передачи всей информации. Соответственно, при асинхронном чтении ты или ждешь пока придут все данные либо пока с той стороны соединение не будет закрыто.

anonymous
()
Ответ на: комментарий от anonymous

ещё могут использоваться разделители, например, перевод строки, если протокол текстовый (ftp, irc)

unC0Rr ★★★★★
()
Ответ на: комментарий от dmitry_vk

Подключать внешние парсеры - оверкилл, по-моему. Тут родного кода на полэкрана максимум.

AngryElf ★★★★★
() автор топика
Ответ на: комментарий от shelA

> В асинхронном режиме пиши данные в достаточно большой буфер. Алгоритм: ищешь в буфере пакет, нашел — забираешь и «сдвигаешь буфер» влево на длину пакета, ненашел - ставишь на пуллинг. И так в цикле пока не найдешь в буфере пакет.

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

AngryElf ★★★★★
() автор топика
Ответ на: комментарий от dmitry_vk

> Вместо recv можно делать select/poll, а затем читать столько байтов, сколько есть у сокета (см. ioctl FIONREAD).

Да, я именно так и делаю.

AngryElf ★★★★★
() автор топика
Ответ на: комментарий от anonymous

> Обычно вышестоящий над TCP протокол либо передает длину пакета, сколько читать (в HTTP это Content-Length), либо просто закрывает соединение после передачи всей информации. Соответственно, при асинхронном чтении ты или ждешь пока придут все данные либо пока с той стороны соединение не будет закрыто.

Поле Content-Length есть не всегда (у меня разные ситуации бывают) ну и его выпарсить тоже как-то надо :)

AngryElf ★★★★★
() автор топика

Я не очень понятно выразился, видимо

Мне интересно, есть ли готовый паттерн/библиотека для такой логики? Как все это руками делается - я сам дошел вполне себе успешно. Но мне этот способ не нравится.

AngryElf ★★★★★
() автор топика
Ответ на: Я не очень понятно выразился, видимо от AngryElf

На своей памяти, я реализую подобный алгоритм уже раз 5-й. И каждый раз без стандартных средств. Но не может же быть, что стандартных средств (т.е., фактически, разбор абстрактного потока на датаграммы) для этого и нету...

AngryElf ★★★★★
() автор топика
Ответ на: комментарий от AngryElf

> должен быть какой-то готовый паттерн работы в таком режиме

Ну а что еще можно тут придумать? Если это HTTP - ищешь в буфере первое вхождение '\r\n', до него соммандная строка. Считываешь ее и «сдвигаешь буфер». Дальше ищешь '\r\n\r\n', до неё строки с хидерами, считаваешь, «сдвигаешь буфер», парсишь. Потом на основе Content-Length, уже читаешь определенное кол-во байт тела. Незнаю, что тут еще можно придумать более эффективного.

shelA
()
Ответ на: комментарий от AngryElf

>> (см. ioctl FIONREAD).

Да, я именно так и делаю.

Необязательно это делать, просто читай всё, что приходит и складывай в буфер.

shelA
()

Если из средств решения рассматриваешь регэкспы, то легко можешь найти обёртку для интерфейса типа readline() и читать данные из сокета построчно.

mikki
()
Ответ на: комментарий от AngryElf

> Поле Content-Length есть не всегда

Если я правильно помню RFC, то только в том случае если тела нет или применен чункененкодинг. В противном случае соединение можно смело сбрасывать с кодом 411.

shelA
()
Ответ на: комментарий от AngryElf

> Ну ок, нет ничего стандартного, значит нет :)

Ну в принципе если нет желания придумывать велосипед есть масса готовых либ, которые этим занимается. Какой язык?

shelA
()
Ответ на: комментарий от mikki

> Всё есть, только как-то странно спрашивать стандартные средства не называя своего ЯП.

Стандартные алгоритмы реализованы для всех языков :)

Python.

AngryElf ★★★★★
() автор топика
Ответ на: комментарий от shelA

Либы обычно готовы под стандартный протокол, например, HTTP. А мне бы абстрактное решение :)

Вот пара примеров:

1. event socket из freeswitch. Там идет обмен датаграммами, асинхронно, хаотично. Датаграммы разделены разделителем типа ================

2. Сокет к gsm-гейту. Обмен AT-командами. Разделитель - перевод строки.

3. HTTP-поток от icecast (заголовок, потом данные, каждые 16000 байт перемежающиеся блоком метаданных).

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

AngryElf ★★★★★
() автор топика
Ответ на: комментарий от shelA

Еще раз - мне не нужна либа для протокола (их нету на все мои запросы), а нужна реализация алгоритма. Т.е. паттерн. Нету такого паттерна - значит нету.

AngryElf ★★★★★
() автор топика
Ответ на: комментарий от AngryElf

> (хочется поэстетствовать)

Ты хочешь странного ))) Посмотри на Twisted, там алогитм протокола выделен как бы в отдельную сущность. Вроде бы как раз то чего тебе хочется.

shelA
()
Ответ на: комментарий от shelA

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

О, спасибо.

AngryElf ★★★★★
() автор топика
Ответ на: комментарий от AngryElf

Стандартные алгоритмы реализованы для всех языков :)

Что за вздор. twisted уже посоветовали, если нужно менее тяжёлое решение, то можно так:

f = mysock.makefile()

while True:
  line = f.readline()
  ...
mikki
()
Ответ на: комментарий от AngryElf

Что за проблема парсить HTTP ? Сначала читаешь заголовки до \r\n\r\n, если есть Content-Length, то читаешь тело и в зависимости от HTTP 1.1 или 1.0, Connecttion: close есть/нет закрываешь соединение либо нет. Если нет Content-Length, то тело читать надо до упора, пока та сторона сама не закроет соединение.

Reset ★★★★★
()
Ответ на: комментарий от Reset

Во, еще кейворд... :)

Понятно, что тривиально, но когда в N-й раз её делаешь, хочется уже чего-то стдлибанутого.

AngryElf ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.