Архив: Технология создания плагинов

Здесь обсуждаются технические аспекты создания дополнений.
Cilvay
Сообщения: 816
Зарегистрирован: 14:55, 16.06.2008

Re: Технология создания плагинов

Сообщение Cilvay »

Ну все равно, то через раз, то 5 мин (блин я то думаю, че за хрень я указал единицу а забанило на 5 минут)
ZigZagkms
Сообщения: 109
Зарегистрирован: 08:23, 11.12.2008
Откуда: Комсомольск-на-Амуре
Контактная информация:

Re: Технология создания плагинов

Сообщение ZigZagkms »

Пожалуйста, помогите разобраться, срочно надо!

2 потока.

1 поток (основной)
- PluginStart(...)
- PluginStop()
- PluginProcess(...)
- PluginGetData(...)

2 поток (создается в PluginStart)
- бесконечный while(1)


Теперь представим ситуацию.
Во втором потоке в цикле в определенный момент времени произойдет вызов (*CommFortProcess)(...), в этот же момент произойдет событие в чате и вызовется функция PluginProcess(...) в первом потоке. Т.к. выполнение (*CommFortProcess)(...) не успевает завершится - зависает сервер комфорта (может быть наоборот, в момент работы функции PluginProcess(...) вызывается во втором потоке (*CommFortProcess)(...)).

Как решить эту проблему? Поместить в 1 поток бесконечный цикл нельзя, отказаться от него нельзя, оконные сообщения не подходят ибо и окна нет и в первом потоке останавливаться нельзя... Организовывать очередь для функции и выполнять события из очереди первым потоком слишком ресурсозатратно...
Забросил, всем спасибо, исходники раздаю кому надо https://github.com/ZigZagkms
$teelR@t
Сообщения: 348
Зарегистрирован: 19:14, 30.03.2008
Откуда: Украина
Контактная информация:

Re: Технология создания плагинов

Сообщение $teelR@t »

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

Maxim Mirgorodsky, а что на счет дебага плагинов? Может все-таки сделаете какую-то урезанную версию для разрабов, без Themida'ы? А то трудновато дебажить :(
KGB писал(а):У меня на триальной версии банит нормально. Ограничение только в сроке бана (всегда 5 мин).
Блин, а я то думаю - почему какое бы время не ставлю, все равно 5 минут)))...
ZigZagkms
Сообщения: 109
Зарегистрирован: 08:23, 11.12.2008
Откуда: Комсомольск-на-Амуре
Контактная информация:

Re: Технология создания плагинов

Сообщение ZigZagkms »

$teelR@t писал(а):ZigZagkms, так как ты явно не используешь синхронизацию процессов (да и в dll она практически не возможна), то работая с двумя потоками функция PluginProcess, вызванная в обеих потоках в одно и тоже время, выполняется параллельно. А это уже получается как "обезьяна с гранатой": неизвестно в какое время произойдет сбой. Так что в таких случаях надо внимательно следить за целостностью данных: использовать только локальные переменные функции или сделать очередь. Самый простой способ создания очереди - используешь переменную-флаг для определения: закончилось ли выполнение предыдущей копии функции или нет. Если закончилось - выполняешь новую копию, если нет, то ждешь пока флаг не будет обозначать, что выполнение закончилось.
А вообще я тоже использую многопоточность и у меня таких проблем не возникало...
Во втором потоке я не использую функцию PluginProcess, во втором потоке вызывается импортируемая функция CommFortProcess. Сервер сам вызывает PluginProcess, как только происходит какое либо событие в чате. Не правильно ты понял суть моего вопроса. Про проблемы с синхронизацией знаю, где надо использую критические секции, но в данном случае не нашел способа тут это применить, это надо применять в самом сервере, чтобы он не вызывал функцию PluginProcess пока отрабатывает импортируемая функция (*CommFortProcess).
Плагин работает на сервере где онлайн достигает 2000 пользователей, события возникают ежесекундно, и то проблема за трое суток выявила себя 2 раза, когда более менее нагрузку дал на функцию (*CommFortProcess).. (передал картинку, пока функция отрабатывала свое, а это миллисекунды, возникло событие в чате, вероятность мала, но все же присутствует, произошло зависание...)
Забросил, всем спасибо, исходники раздаю кому надо https://github.com/ZigZagkms
$teelR@t
Сообщения: 348
Зарегистрирован: 19:14, 30.03.2008
Откуда: Украина
Контактная информация:

Re: Технология создания плагинов

Сообщение $teelR@t »

ZigZagkms, тогда у тебя получается, что вся обработка событий от сервера выполняется в одном потоке, а судя по тому, что сервер и плагин работают в одном потоке, то в теории события должны выполняться по очереди. Значит проблемы на стороне сервера, он просто не справляется с нагрузкой...
ZigZagkms
Сообщения: 109
Зарегистрирован: 08:23, 11.12.2008
Откуда: Комсомольск-на-Амуре
Контактная информация:

Re: Технология создания плагинов

Сообщение ZigZagkms »

$teelR@t писал(а):ZigZagkms, тогда у тебя получается, что вся обработка событий от сервера выполняется в одном потоке, а судя по тому, что сервер и плагин работают в одном потоке, то в теории события должны выполняться по очереди. Значит проблемы на стороне сервера, он просто не справляется с нагрузкой...
Нет, дело не в этом, даже если в функции PluginProcess нету обработки событий вообще все равно виснет, проблема не в этом.
Забросил, всем спасибо, исходники раздаю кому надо https://github.com/ZigZagkms
Maxim Mirgorodsky
Администратор
Сообщения: 6890
Зарегистрирован: 09:56, 27.06.2005

Re: Технология создания плагинов

Сообщение Maxim Mirgorodsky »

ZigZagkms писал(а):Вопрос к Maxim Mirgorodsky.

Скажите пожалуйста, как происходит фильтрация поступающих данных, полная или частичная, допустим если мы подадим в функцию неверную инфу как ее обрабатывает сервер?
Например, я посылаю серверу событие с ID 1001, а заместо блока данных подаю 4 байта (FF FF FF FF) (т.е. создаю ошибку, предоставляю ложную длину текста, причем невозможную) - плагин останавливается. Т.е. он словил исключение о невозможности прочитать данные или как там работает этот механизм?

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

Было бы здорово если бы сервер просто игнорировал все ошибочные данные, не останавливая плагин..
В случае подачи некорректных данных программа не зависнет.

С Вашим случаем разобрались по ЛС, причина в некорректно реализованной многопоточности.
@serg@ писал(а):
ZigZagkms писал(а):Вопрос к Maxim Mirgorodsky.

Скажите пожалуйста, как происходит фильтрация поступающих данных, полная или частичная, допустим если мы подадим в функцию неверную инфу как ее обрабатывает сервер?
Например, я посылаю серверу событие с ID 1001, а заместо блока данных подаю 4 байта (FF FF FF FF) (т.е. создаю ошибку, предоставляю ложную длину текста, причем невозможную) - плагин останавливается. Т.е. он словил исключение о невозможности прочитать данные или как там работает этот механизм?

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

Было бы здорово если бы сервер просто игнорировал все ошибочные данные, не останавливая плагин..
Коммфорт не может проверить FF FF FF FF это ошибка или действительно имя такой длинны, поэтому пытается прочитать все данные, при этом происходит ошибка прав доступа, идёт чтение ложных данных, и при попытке обработать естественно вызывается ошибка, которую тупо проигнорировать опасно. В этом случае лучше передавать в плане длинны заведомо корректные значения (что не так сложно), и следить внимательно за соблюдением порядка данных (что намного сложней), в этом случае коммфорту придётся отслеживать только корректность текста.
Но более точно тебе ответит естественно Maxim.
Программа проверяет данные на соответствие объему. В случае несоответствия разумеется нормальная работа плагина не будет возможной, но на работу программы это не повлияет.
ZigZagkms
Сообщения: 109
Зарегистрирован: 08:23, 11.12.2008
Откуда: Комсомольск-на-Амуре
Контактная информация:

Re: Технология создания плагинов

Сообщение ZigZagkms »

Максим, здравствуйте.
Сообщение было опубликовано после обращения в приват, мне не удалось решить проблему самому решил спросить совета у всех как это можно сделать. Синхронизации не помогают (ткните пальцев как их применить в моей проблеме возьму свои слова назад и скажу большое спасибо), один выход из проблемы - использовать импортируемую функцию только в главном потоке CommFortProcess (как Вы и сказали что ее можно использовать только в главном потоке), я подумал создать очередь запросов к функции которая наполняется вторым потоком, НО главный вопрос как обслуживать (выталкивать и исполнять) эту очередь в первом потоке? Ведь WaitObject подобные функции мы использовать не можем, зависания в главном потоке недопустимы... Тоже самое кстати относится и ко второй импортируемой функции, тут очередь сложно применима, т.к. необходимо еще и получать результаты выполнения.
Проблема остается открытой, советов от участников конференции кроме как "у меня и так работает" не было получено. Помогите пожалуйста разобраться и я отстану!)

Хорошо, вот код для понимания моей проблемы
Поток 1:

Код: Выделить всё

// Прием событий
VOID PluginProcess(DWORD dwID, BYTE * bInBuffer, DWORD dwInBufferSize)
{
	// Что угодно
}
Поток 2:

Код: Выделить всё

DWORD WINAPI fNetThread(LPVOID lpParam)
{
	while (true)
	{
		// Замираем на 200ms
		Sleep(200)
		
		// Произошло какое то внешнее событие
		if (nowdo)
		{
			(*CommFortProcess)(dwPluginID, dwID, data, data_len);
		}
	}
}
И вот, если в первом произойдет событие и не успев отработаться "// Что угодно" выполнится во втором (*CommFortProcess) произойдет зависание. Вариант два, с синхронизацией (единственное что я смог придумать, реализация не верная, в этом случае зависания происходят либо как в первом варианте либо зависает плаг)
Поток 1:

Код: Выделить всё

// Прием событий
VOID PluginProcess(DWORD dwID, BYTE * bInBuffer, DWORD dwInBufferSize)
{
	EnterCriticalSection(&cs);
	// Что угодно
	LeaveCriticalSection(&cs);
}
Поток 2:

Код: Выделить всё

DWORD WINAPI fNetThread(LPVOID lpParam)
{
	while (true)
	{
		// Замираем на 200ms
		Sleep(200)
		
		// Произошло какое то внешнее событие
		if (nowdo)
		{
			EnterCriticalSection(&cs);
			(*CommFortProcess)(dwPluginID, dwID, data, data_len);
			LeaveCriticalSection(&cs);
		}
	}
}
Как помоему то проблему можно решить добавлением критических секций в исходном коде самого сервера, не выполнять вызов функции, и исчезнет проблема с использованием импортируемых функций в потоках. I need help! :cry:
Забросил, всем спасибо, исходники раздаю кому надо https://github.com/ZigZagkms
KGB
Сообщения: 659
Зарегистрирован: 08:54, 13.07.2010
Откуда: Чебоксары, Россия
Контактная информация:

Re: Технология создания плагинов

Сообщение KGB »

ZigZagkms писал(а):Максим, здравствуйте.
один выход из проблемы - использовать импортируемую функцию только в главном потоке CommFortProcess (как Вы и сказали что ее можно использовать только в главном потоке), я подумал создать очередь запросов к функции которая наполняется вторым потоком, НО главный вопрос как обслуживать (выталкивать и исполнять) эту очередь в первом потоке?
Предложу один вариант, хотя он может быть и неверным. Заполнение и вытаскивание элементов из очереди - критические секции (это и так понятно). Можно воспользоваться таймером, созданным в основном потоке (интервал - 1мс). При инициализации отключаем его (Enabled = False). Последним действием при добавлении элемента в очередь - включаем. При обработке события OnTimer - вытаскивание элемента, если очередь не пуста или отключение таймера, если она пуста.
Время ожидания добавления элемента в очередь мне кажется не особо критичным в этом случае.
Если неправ, просьба не бить, только учусь :D
ZigZagkms
Сообщения: 109
Зарегистрирован: 08:23, 11.12.2008
Откуда: Комсомольск-на-Амуре
Контактная информация:

Re: Технология создания плагинов

Сообщение ZigZagkms »

KGB писал(а):Предложу один вариант, хотя он может быть и неверным. Заполнение и вытаскивание элементов из очереди - критические секции (это и так понятно). Можно воспользоваться таймером, созданным в основном потоке (интервал - 1мс). При инициализации отключаем его (Enabled = False). Последним действием при добавлении элемента в очередь - включаем. При обработке события OnTimer - вытаскивание элемента, если очередь не пуста или отключение таймера, если она пуста.
Время ожидания добавления элемента в очередь мне кажется не особо критичным в этом случае.
Если неправ, просьба не бить, только учусь :D
Формы у меня в проекте нету. Насколько мне известно таймер он посылает сообщение окну WM_TIMER и тогда уже выполняется функция, была бы форма было бы проще посылать сообщения самому со своим идентификатором и выполнять, хотя в этом я тоже могу ошибаться, не долго я с С++ работаю, как раз с тех самых пор когда разработчики отказались от UDP=) Добавлять форму ради таймера не охота, слишком большие затраты.
Забросил, всем спасибо, исходники раздаю кому надо https://github.com/ZigZagkms
KGB
Сообщения: 659
Зарегистрирован: 08:54, 13.07.2010
Откуда: Чебоксары, Россия
Контактная информация:

Re: Технология создания плагинов

Сообщение KGB »

ZigZagkms писал(а): Формы у меня в проекте нету. Насколько мне известно таймер он посылает сообщение окну WM_TIMER и тогда уже выполняется функция, была бы форма было бы проще посылать сообщения самому со своим идентификатором и выполнять, хотя в этом я тоже могу ошибаться, не долго я с С++ работаю, как раз с тех самых пор когда разработчики отказались от UDP=) Добавлять форму ради таймера не охота, слишком большие затраты.
Создавать таймер можно и в процессе выполнения, в том числе без родительской формы. От сообщений, конечно, никуда не деться, но в этом случае таймер создаёт собственное окно. И затраты будут только на него (вот тут уже я могу ошибаться).
Аватара пользователя
Aarts
Сообщения: 3
Зарегистрирован: 19:26, 11.11.2010

Re: Технология создания плагинов

Сообщение Aarts »

@serg@ писал(а):

Код: Выделить всё

   AnsiString aData;//буфер
	int iSize = (*CommFortGetData)(12, dwID, NULL, NULL, NULL, NULL); //получаем объем буфера
	aData.SetLength(iSize);
	(*CommFortGetData)(12, dwID,aData.c_str(),iSize, NULL, NULL);//заполняем буфер
	int rOffset=0;
	fReadText(aData.c_str(),&rOffset);//тут имя твоего пользователя
Блин. Ёлки-палки, лес дремучий.


var aData : AnsiString;
iSize:=CommFortGetData(12, 0, 0, 0, 0);
SetLength(aData, iSize);
CommFortGetData(12, aData, iSize, 0, 0);
fReadText(aData, iOffset);

Ему не нравится aData. Вот что пишет:
[Error] main.pas(339): Incompatible types: 'String' and 'PAnsiChar'
[Error] main.pas(340): Incompatible types: 'String' and 'PAnsiChar'
Вот сама функция:
TCommFortGetData = function(dwID : DWORD; bInBuffer : PAnsiChar; dwInBufferSize : DWORD; bOutBuffer : PAnsiChar; dwOutBufferSize : DWORD): DWORD; stdcall;

Что-то с этим aData. Беда. Ну ясен пень, надо его в PAnsiChar загнать. Ведь так? Или что я не так делаю?
Дайте мне ещё слова напутствия, пожалуйста (:
http://mediumeex.livejournal.com
@serg@
Сообщения: 702
Зарегистрирован: 14:50, 20.07.2009

Re: Технология создания плагинов

Сообщение @serg@ »

Aarts писал(а): iSize:=CommFortGetData(12, 0, 0, 0, 0);
SetLength(aData, iSize);
CommFortGetData(12, aData, iSize, 0, 0);
fReadText(aData, iOffset);
С дельфи не проконсультирую, ошибка точно в первом вызове. Тут во втором параметре должен быть ID плагина. fReadText, это пример функции, который был в NULL-плагине, на начальном этапе я не стал её переделывать.

Код: Выделить всё

UnicodeString fReadText(BYTE * bInBuffer, int * iOffset)
{//вспомогательная функция для упрощения чтениея Unicod
	int iLength;
	memcpy(&iLength, bInBuffer + (*iOffset),4);
	(*iOffset)+=4;
	UnicodeString uRet;
	uRet.SetLength(iLength);
	memcpy(uRet.c_str(), bInBuffer + (*iOffset),iLength*2);
	(*iOffset)+=iLength*2;
	return uRet;
}
На первых страницах ветки были примеры на дельфи, советую в них порыться.
ОреЛ
Сообщения: 376
Зарегистрирован: 11:18, 11.07.2008
Откуда: Ульяновск
Контактная информация:

Re: Технология создания плагинов

Сообщение ОреЛ »

Maxim Mirgorodsky писал(а): Изменения по системе плагинов в сервере 5.03:
...
- Исправлена ошибка при получении данных от программы с ID=1080;
...
Вопрос ко всем, в том числе и к самому Максиму:
проверял ли кто работу получения данных от программы с ID=1080? или я что-то не так делаю, либо действительно приходят неверные данные.
UPD: однажды получилось правильно написать процедуру получения списка, но потом опять всё свалилось... печалька. дайте код на С++
UPD2:

Код: Выделить всё

	int size = VirtualUser.Length() * 2 + 4;
	int len = VirtualUser.Length() * 2;
	BYTE * bOutBuffer = new BYTE[size];
	memcpy(bOutBuffer, &len, 4);
	memcpy(bOutBuffer + 4, VirtualUser.c_str(), len * 2);
	int dwInBufferSize = (*CommFortGetData)(dwPluginID, 1080, 0, 0, bOutBuffer, size);
	BYTE * bInBuffer = new BYTE[dwInBufferSize];
	(*CommFortGetData)(dwPluginID, 1080, bInBuffer, dwInBufferSize, bOutBuffer, size);
	int countChannels;
	memcpy(&countChannels, bInBuffer, 4);
Что не так?
UPD3:
Каковы должны быть условия для правильно работы? Может быть, уже подключенный виртуальный пользователь или еще что-то? И эта функция вообще работает на триальных версиях сервера?
Последний раз редактировалось ОреЛ 16:09, 19.11.2010, всего редактировалось 3 раза.
Когда пишете программу, всегда думайте о том, что её может затем поддерживать психопат и насильник, который знает где вы живёте.
— Martin Golding
KGB
Сообщения: 659
Зарегистрирован: 08:54, 13.07.2010
Откуда: Чебоксары, Россия
Контактная информация:

Re: Технология создания плагинов

Сообщение KGB »

В 5.00 не работала, в 5.03 работает, проверял.
Закрыто