Проверка трубопроводов C, проходящих через программу - пограничные случаи - PullRequest
0 голосов
/ 25 мая 2009

Я получаю из сокета A и записываю это в сокет B на лету (как прокси-сервер). Я хотел бы проверить и, возможно, изменить данные, проходящие через. Мой вопрос заключается в том, как обрабатывать граничные случаи, т. Е. Когда искомое регулярное выражение будет соответствовать двум последовательным итерациям чтения сокета A и записи сокета B.

char buffer[4096]
int socket_A, socket_B

/* Setting up the connection goes here */

for(;;) {

    recv(socket_A, buffer, 4096, 0);

    /* Inspect, and possibly modify buffer */

    send(socket_B, buffer, 4096, 0);

    /* Oops, the matches I was looking for were at the end of buffer,
     * and will be at the beginning of buffer next iteration :( */

}

Ответы [ 6 ]

1 голос
/ 25 мая 2009

Вы должны знать и / или сказать что-то о своем регулярном выражении.

В зависимости от регулярного выражения, вам может понадобиться намного больше буферизовать, чем сейчас.

В худшем случае может быть что-то вроде регулярного выражения, которое говорит: «найди все, начиная с самого начала и до первого появления слова« собака », и замени это на что-нибудь другое»: если у тебя есть регулярное выражение Таким образом, вы должны буферизовать (без пересылки) все, начиная с начала и до первого появления слова «собака»: что может никогда не произойти, т. е. может быть бесконечное количество для буферизации.

1 голос
/ 25 мая 2009

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

  1. Буфер (удерживать) поступающие данные до тех пор, пока не будет достигнута граница. Границей может быть конец строки, конец записи или любой другой способ, которым вы знаете, что ваше регулярное выражение будет соответствовать.
  2. Когда «запись» готова, обработайте ее и поместите результаты в выходной буфер.
  3. Записать все, что накоплено в буфере вывода.

Это обрабатывает большинство случаев. Если у вас есть один из редких случаев, когда на самом деле нет «записи», вам нужно создать какой-то конечный автомат (DFA). Под этим я подразумеваю, что вы должны быть в состоянии накапливать данные до тех пор, пока либо а) они не смогут соответствовать вашему регулярному выражению, либо б) это не будет завершенное совпадение.

EDIT: Если вы сопоставляете фиксированные строки вместо истинного регулярного выражения, тогда вы сможете использовать алгоритм Boyer-Moore , который на самом деле может выполняться за сублинейное время (пропуская символы). Если вы все сделаете правильно, при перемещении по входу вы можете по ходу выбросить ранее просмотренные данные в выходной буфер, уменьшив задержку и значительно увеличив пропускную способность.

1 голос
/ 25 мая 2009

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

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

1 голос
/ 25 мая 2009

Мое предложение: иметь два буфера и вращаться между ними:

  1. Буфер Recv 1
  2. Буфер Recv 2
  3. Процесс.
  4. Буфер отправки 1
  5. Буфер записи 1
  6. Процесс, но с буфером 2 перед буфером 1.
  7. Буфер отправки 2
  8. Перейти к 2.

или что-то подобное?

0 голосов
/ 25 мая 2009

Одной из альтернатив является использование poll(2) -подобной стратегии с неблокирующими сокетами. При чтении события захватите буфер из сокета, поместите его во входящую очередь, вызовите lexer / parser / matcher, который собирает буферы в поток, а затем помещает куски в выходную очередь. При событии записи возьмите кусок из очереди вывода, если он есть, и запишите его в сокет. Это звучит довольно сложно, но на самом деле это не так, как только вы привыкнете к инвертированной модели управления.

0 голосов
/ 25 мая 2009

По сути, проблема с вашим кодом заключается в том, что цикл recv / send работает на более низком сетевом уровне, чем ваши модификации. То, как вы решите эту проблему, зависит от того, какие изменения вы вносите, но, вероятно, это связано с буферизацией данных до тех пор, пока не будут выполнены все локальные изменения.

РЕДАКТИРОВАТЬ: Я не знаю ни одной библиотеки регулярных выражений, которая может фильтровать поток, как это. Насколько это будет сложно, зависит от вашего регулярного выражения и протокола, который он фильтрует.

...