![]() |
Модераторы: Snowy, bartram, MetalFan, bems, Poseidon, Riply |
![]() ![]() ![]() |
|
vogel |
|
||||
Новичок Профиль Группа: Участник Сообщений: 10 Регистрация: 12.12.2007 Репутация: нет Всего: нет |
Уважаемые коллеги.
Возникла у меня проблема при обработке большго потока данных. Описываю ситуацию : 1. Есть DLL-ка, которая внедряется в чужой процесс, хукает send и recv и передаёт перехваченный трафик главному приложению при помощи WM_COPYDATA вот как-то так :
2. Есть основное приложение, которое получает это сообщение и обрабатывает полученный пакет. ВОт примерно так :
И всё это работает прекрасно, если приложение-жертва посылает пакеты достаточно редко. Как только приложение-жертва начинает активный обмен по сети, причём достаточно большими объёмами данных - возникают проблемы : self.LogPacket(ipcMessage.Operation, packetBuffer), которая занимается идентификацией и разбором пакетов отрабатывает долго и приложение падает с 'Stack Overflow' в TfmMain.WMCOPYDATA(var Msg: TWMCopyData); Внимание вопрос : Каким образом реализовать такую обработку событий, чтобы пришедшие данный быстро-быстро копировались в какое-либо временное хранилище и мы быстро-быстро выходили из TfmMain.WMCOPYDATA. В то время как некий тред в фоновом режими уже занимался разгребанием и логированием пакетов. Очень расчитываю на помощь знающих людей. |
||||
|
|||||
ne0n |
|
|||
PlayBoy ![]() ![]() Профиль Группа: Участник Сообщений: 733 Регистрация: 5.8.2005 Где: Н.Новгород Репутация: 4 Всего: 11 |
ну собственно что первое приходит в голову это реализовать подобие клиент-сервера.
Т.е. отправила dll данные-> ждем-> приоложение получает данные, обрабатывает их-> отправляет потвержение того что все обработано нашей dll библиотеке-> при получение подтверждения обработки, dll отправляет следующию порцию данных итд. Вроде так никаких переполнений возникнуть не должно... Это сообщение отредактировал(а) ne0n - 12.7.2008, 01:47 |
|||
|
||||
vogel |
|
|||
Новичок Профиль Группа: Участник Сообщений: 10 Регистрация: 12.12.2007 Репутация: нет Всего: нет |
Спасибо, но ткаой вариант не пойдёть. Ибо в этом случа мы спровоцируем лаги в приложении-жертвне, в котором захуканы send и recv.
Попробую переформклирвать вопрос : Каким образом я могу вызывать процедуру асинхронно ? То есть вызывать self.LogPacket(ipcMessage.Operation, packetBuffer) таким образом, чтобы не ждать окончания выполнения, а переходить сразу дальше. Чувствую, что это можно сделать через потоки, но пока не понимаю как... |
|||
|
||||
CodeMonkey |
|
|||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1839 Регистрация: 24.6.2008 Где: Россия, Тверь Репутация: 16 Всего: 89 |
Я бы сделал так: выделил бы буфер в разделяемой памяти (CreateFileMapping). При получении данных писал бы в этот буфер, затем - отправлял бы уведомление другой программе (например через SetEvent) и сразу же выходил бы из функции.
Другая программа имела бы один или несколько потоков, которые бы ждали наступления события (WaitForSingleObject) и как только оно получено - начинали обработку блока. После того, как блок обработан - пометить его свободным и отправлять уведомление первой программе (наверное, опять через SetEvent). Нужно только отслеживать, какая часть общего буфера свободна, а какая - занята (проще всего делать это циклически, т.е. ввести маркер начала необработанных данных и конца, при достижении конца буфера - начинать запись с его начала). Если данные прибывают чаще, чем вторая программа успевает их обработать, то надо, либо отбрасывать часть данных, либо выделять дополнительный буфер, либо приостанавливать приём-передачу, пока вторая программа не обработает данные. Для последнего и нужен был второй SetEvent - в случае, если места в буфере мало, то первая программа может ждать на этом событии, пока вторая её не уведомит, что в буфере появилось новое свободное место. -------------------- Опытный программист на C++ легко решает любые не существующие в Паскале проблемы. |
|||
|
||||
Riply |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Комодератор Сообщений: 572 Регистрация: 27.3.2007 Где: St. Petersburg Репутация: 21 Всего: 32 |
Я делала подобное (данные надо было получать сразу от нескольких процессов) используя Pipe - ы. А как только ты определился с "каналом связи" далее все будет ограничиваеться только воображением. Вариантов масса : от ReadFileEx (например, с очередью) до пула потоков. ![]() P.S. Слишком обще сформулирована задача, вот и получаются общие ответы. ![]() |
|||
|
||||
vogel |
|
|||
Новичок Профиль Группа: Участник Сообщений: 10 Регистрация: 12.12.2007 Репутация: нет Всего: нет |
Хммм, почему слишком обще ??
Я же в первом посте написал по поводу "канала связи" - это WM_COPYDATA. Вопрос в том, что обработка принятых данных (на некоторых этапах) идёт медленне, чем эти данные поступают. Из-за этого stack overflow. Мне самому видится такое решение : 1. Обработчик WM_COPYDATA складывает полученные строки, например, в TStringList и тутже возвращает управление - этим мы достигаем быстроту ответов и избавляемся от stack ovwrflow (как мне кажется). 2. При этом отдельный TThread мониторит этот самый TStringList на предмет count > 0 и елси оно так - выгребает строку и обрабатывает её. главный вопрос теперь - критические секции - как бы не потерять время на них... |
|||
|
||||
Rennigth |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1708 Регистрация: 21.6.2004 Где: Moscow Репутация: 8 Всего: 76 |
vogel, Логичнее будет использовать пул потоков.
-------------------- (* Honesta mors turpi vita potior *) |
|||
|
||||
vogel |
|
|||
Новичок Профиль Группа: Участник Сообщений: 10 Регистрация: 12.12.2007 Репутация: нет Всего: нет |
Спасибо за идею, мне б примерчик. А вообще по теме вопроса - проблему решил : 1. Надо было сделать все переменные в обработчике WM_COPYDATA глобальными - это сняло проблему stack overflow 2. Данные заносятся в TStringList и обрабатываются отдельным потоком - это снялдо проблему производительности. Спасибо всем участникам за обсуждение, однако пока не закрываю - жду интересных решений. Желательно с примерами. Вот пул потоков очень даже заинтересмоало. |
|||
|
||||
CodeMonkey |
|
||||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1839 Регистрация: 24.6.2008 Где: Россия, Тверь Репутация: 16 Всего: 89 |
Причём тут stack overflow вообще? ![]()
У вас получается, что вы будете гонять данные впустую между различными буферами. Не самое изящное решение. Для межпроцессного взаимодействия лучше использовать специально предназначенные для этого IPC функции, а вовсе не сообщения Windows, предназначенные (в первую очередь, конечно) для визуального интерфейса. Добавлено через 48 секунд
Не сняло, а скрыло ;) -------------------- Опытный программист на C++ легко решает любые не существующие в Паскале проблемы. |
||||
|
|||||
vogel |
|
||||||
Новичок Профиль Группа: Участник Сообщений: 10 Регистрация: 12.12.2007 Репутация: нет Всего: нет |
Ну Вы внимательно прочитайте первое сообщение топика - Вам сразу станет понятно причём оно тут.
Я весь внимание. Очень хочется посмотреть, что же именно вы понимаете под IPC ?? MMF, NamedPipes, UDP ?? Кстати, WM_COPYDATA в частности относится к механизмам IPC.
Сняло. Ибо память под локальные переменные внутри процедур отводится из стека. А скрыть эту проблему нельзя - у вас либо переполняется стек либо нет. Это сообщение отредактировал(а) vogel - 14.7.2008, 16:23 |
||||||
|
|||||||
Rennigth |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1708 Регистрация: 21.6.2004 Где: Moscow Репутация: 8 Всего: 76 |
Вот тут можно почитать про встроеммые механизмы винды. Если не подойдет, надо самому организовывать, и затачивать под Ваши задачи. -------------------- (* Honesta mors turpi vita potior *) |
|||
|
||||
CodeMonkey |
|
||||||||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1839 Регистрация: 24.6.2008 Где: Россия, Тверь Репутация: 16 Всего: 89 |
Что ж вы такой непонятливый.
Ладно, не будем намёками. Будем правду резать ![]() Смотрите:
Как это работает: приходит сообщение WM_COPYDATA, вы запускаете обработку данных, затем вызывается Application.ProcessMessages, который просматривает накопившиеся в очереди сообщения и запускает их обработчики. Если вам сообщения WM_COPYDATA поступают быстрее, чем вы их обрабатываете, то это значит, что на момент вызова Application.ProcessMessages в очереди окна уже есть одно или более сообщений WM_COPYDATA. Что значит, что обработчик WMCOPYDATA будет запускаться из Application.ProcessMessages, который в свою очередь вызывается из... правильно, WMCOPYDATA. Видите рекурсию? У вас получается такое дерево вызовов:
Причём чем больше разница в скорости прибытия/обработки сообщений, тем быстрее растёт вложенность вызовов. Вот вам и причина переполнения стека - рано или поздно стек закончится из-за большой рекурсии вызовов. Разумеется, если в потоке входящих данных появляется окно (замедление скорости данных), то начинается процесс выхода из дерева вызовов, и стек освобождается. И то, что вы вынесли переменные за пределы процедуры принципиально не меняет ситуации - это лишь меняет скорость заполнения стека (в стек при каждом вызове попадает меньше информации, но, тем не менее, всё ещё попадает). Да, это уменьшает вероятность переполнения стека, т.к. теперь стек заполняется дольше, а значит больше вероятность появления окна во входящих данных, чтобы стек успел освободиться. Тем не менее проблему вы не сняли, она всё ещё тут - достаточно поддерживать высокий темп входящих данных и стек рано или поздно снова переполнится. Именно поэтому я сказал, что проблему вы скрыли, а не решили. Теперь ситуация ясна? Убедите Application.ProcessMessages (зачем вы его вообще туда всунули?) и проблема решена. Поэтому я и сказал: причём тут переполнение стека, когда проблемы нет вообще - вы её просто специально создали.
Посмотрите, вам уже предлагали воспользоваться MMF или пайпами. Формально, да, WM_COPYDATA относится к IPC. Проблема в том, что оно ориентировано на простую пересылку данных между двумя GUI-приложениями. Поскольку фактически это сообщение является обёрткой вокруг MMF, то для вашего сценария нет смысла нагружать приложение дополнительными накладными расходами. Впрочем, это ваш выбор. -------------------- Опытный программист на C++ легко решает любые не существующие в Паскале проблемы. |
||||||||
|
|||||||||
vogel |
|
|||
Новичок Профиль Группа: Участник Сообщений: 10 Регистрация: 12.12.2007 Репутация: нет Всего: нет |
CodeMonkey, Огромнейшее спасибо за подробное объяснение такому вот непонятливому.
Проблема действительно решилась. А я был неправ. Теперь "раскуриваю" MMF, но оно меня как-то пугает... Через WM_COPYDATA всё происходит достаточно лего и просто |
|||
|
||||
CodeMonkey |
|
|||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1839 Регистрация: 24.6.2008 Где: Россия, Тверь Репутация: 16 Всего: 89 |
Если вас устроит ваша реализация с WM_COPYDATA - ради бога, используйте её. Просто она будет не самой оптимальной (в плане быстродействия). Зато (наверное) наиболее просто реализуемой.
И, кстати, стек потоков - правильность этого решения зависит от того, какие именно действия вы выполняете в процедуре обработки. Например, если у вас поток занимается тем, что производит какие-либо вычисления по полученным данным (т.е. работа идёт в основном процессором), то выделение дополнительных потоков просто ничего не даст, а наоборот - ухудшит ситуацию. Если же в обработке вы, скажем, просто копируете данные в файл, то тут процессор почти не участвует, и использование нескольких потоков (может) существенно увеличит пропускную способность обработки. Впрочем, даже в этом случае наиболее быстродействующим решением был бы один поток с использованием асинхронного ввода-вывода, но такого монстра вы замучаетесь писать (но если решитесь, то начать стоит отсюда). Это так, информация к размышлению. -------------------- Опытный программист на C++ легко решает любые не существующие в Паскале проблемы. |
|||
|
||||
vogel |
|
|||
Новичок Профиль Группа: Участник Сообщений: 10 Регистрация: 12.12.2007 Репутация: нет Всего: нет |
Реализация с WM_COPYDATA оказалась наиболее простой и понятной, но и у неё есть свои недостсаки - как-то передача блоков только фиксированного рамера. В итоге, зная, что данные у меня длиногй максимум word - пришлось делать массив [0..$FFFF]. Это работает, но приносит дополнительные расходы, ибо размер передаваемых данных всегда разный и в-основном значительно меньше FFFF.
Однако, я не уверен, что через MMF можно будет просто кидать динамические массивы или строки.
Именно это и происходит. Поэтому от потока я уже оказался. Ещё раз спасибо Вам за то, что разъяснили такую грубую ошибку. |
|||
|
||||
![]() ![]() ![]() |
Правила форума "Delphi: WinAPI и системное программирование" | |
|
Запрещено: 1. Публиковать ссылки на вскрытые компоненты 2. Обсуждать взлом компонентов и делиться вскрытыми компонентами
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Snowy, bartram, MetalFan, bems, Poseidon, Rrader, Riply. |
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | Delphi: WinAPI и системное программирование | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |