Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Сети";
Текущий архив: 2004.09.12;
Скачать: [xml.tar.bz2];

Вниз

Остановка TIdTCPServer   Найти похожие ветки 

 
начинающий   (2004-07-04 19:04) [0]

Начал изучать компоненты Indy. И вот какая беда приключилась: чтобы остановить сервер TIdTCPServer делаю следующее:
procedure TForm1.BitBtn2Click(Sender: TObject);
begin
 if IdTCPServer1.Active then
 begin
  IdTCPServer1.Active := FALSE;
  IdTCPServer1.Bindings.Clear;
  Memo1.Lines.Add("[Stopped]")
 end
end;

Но самое интересное дальше: так работает лишь тогда, когда в OnDisconnect ничего не прописано. Стоит лишь мне поместить туда хоть небольшой участок кода, самый безвредный (типа Memo1.Lines.Add), как действие вышеуказанного обработчика приводят к зависанию программы на несколько секунд, после чего выводится информация об исключении, а после чего - собственно и происходит OnDisconnect.
В чём мой прокол? Спасибо!


 
alienserg   (2004-07-06 05:09) [1]

все правильно. самый верный способ остановить сервер это сделать
IdTCPServer1.Active := FALSE;

Твои проблемы в том, что обработчик события OnDisconnect привязан к треду коннекта и выполняется в нем, а не в основном VCL потоке. Поэтому всю VCL работу надо синхронизировать.
Через Synchronize() или с помощью TIdSync или посылая сообщеня окну с помощью PostMessage.
В OnExecute это тоже надо всегда иметь в виду. Более того, не только работа по отрисовке на форме должна синхронизироваться, но и работа с базой.


 
Digitman ©   (2004-07-06 14:20) [2]


> alienserg   (06.07.04 05:09) [1]


> но и работа с базой


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


 
alienserg   (2004-07-06 17:56) [3]

Digitman ©   (06.07.04 14:20) [2]
ну, положим, это ты загнул лишнего

Лучше пересинхронировать чем недосинхронизировать :)
Да, в общем случае это не всегда верно. Всякий раз надо выяснять, являются ли используемые компоненты доступа к базе thread safe. В данный момент работаю с MySQL через ZEOS, и надо самому заботиться о синхронизации. ZEOS не является полностью thread safe. Приходится поддерживать пул экземпляров датамодуля и динамически связывать их с тредами в моменты работы с базой. Это и есть в моем понимании синхронизация. Недопущение одновременной работы с экземпляром компонента из разных тредов.
Кстати, ты не подскажешь, является ли Direct Oracle Access(DOA) tread safe?

начинающий   (04.07.04 19:04)  
Еще один момент, когда надо заботиться о синхронизации:
Клиент1 запросил у сервера например структуру директории.
Сервер шлет ему в ответ 100 строк через WriteLn.
В это же самое время клиент2 шлет клиенту1 сообщение через сервер.
Сервер ищет в списке активних тредов тред клиента1 и использует его TIdPeerThread.Connection для отправки сообщения. Но отправка сообщения клиенту1 от клиента2 всегда выполняется в треде клиента2! Поэтому надо синхронизировать. Иначе строки смешаются или вообще заклинит.
Та же самая ситуация, если ты пытаешься что-то отправить по нажатию кнопки на форме. Или по таймеру опросив базу и найдя записи, которые надо отослать клиенту.
Все синхронизировать.


 
Digitman ©   (2004-07-06 18:05) [4]


> alienserg   (06.07.04 17:56) [3]


> Недопущение одновременной работы с экземпляром компонента
> из разных тредов


ну это и в большинстве других клиентских частях так же выглядит, ничего удивительного нет здесь

но ничто не мешает использовать отдельный набор экземпляров кл.компонентов для доступа к серв.части

в том же БДЕ, который так же не thread-safe, достаточно в отдельном потоке создать отдельную связку TSession+TDatabase+TDataSet - и можно смело обращаться к базе, невзирая на другие потоки, делающие то же самое


> является ли Direct Oracle Access(DOA) tread safe


понятия не имею


 
Digitman ©   (2004-07-06 18:14) [5]


> alienserg   (06.07.04 17:56) [3]


> Все синхронизировать


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

синхронизация с осн.потоком требуется, в 1-ю очередь тогда, когда доп.поток обращается к визуализированным VCL-объектам


 
alienserg   (2004-07-06 19:43) [6]

Digitman ©   (06.07.04 18:14) [5]
синхронизация с осн.потоком требуется
Возможно я не совсем корректно выразился... Мы говорим об одном и том же.
Под синхронизацией я имел ввиду не привязку к VCL-потоку и не столько использование Synchronize(), сколько разруливание конкурентного доступа к объектам из разных тредов. Временный захват ресурса в монопольное пользование или создание_своих/использование_сушествующих очередей сообщений и их обработчиков или создание пула ресурсов и динамическое связывание их с тредами, когда тем нужно.


 
Digitman ©   (2004-07-07 08:11) [7]


> alienserg   (06.07.04 19:43) [6]


ок.. с эти согласен


 
начинающий   (2004-07-07 15:05) [8]

Господа, я чувствую, что в следующем сообщении уже будет пример на ассемблере! Не хочу показаться неблагодарным, но я никак не пойму как конкретно нужно синхронизировать?
Может onDisconnect должен содержать нечто наподобие: AThread.Synchronize(someProcedure). Или использовать критические секции при деактивации сервера? И как использовать упомянутый выше TIdSync(если нужно, конечно)? Он же абстрактный. Может, кто подобрее и посвободнее приведет маленький пример, или даст ссылку на ресурс, который разъяснит мою темноту (хотя и говорят, что во многой мудрости много печали, но и глупцу жить не весело).


 
alienserg   (2004-07-07 19:11) [9]

начинающий   (07.07.04 15:05) [8]

К господам отношения не имеем, мы вольные труженики.

По Indy основной кладезь знаний это ньюсгруппы в atozedsoftware.indy
например newsgroup:atozedsoftware.indy.servers.tcp
их там куча и все полезные. Отвечают сами разработчики, причем очень быстро и всегда.

Вот тебе примеров пара, не на ассемблере.

**** Использование TIdSync ****
type
 TStartFileReceive = class(TIdSync)
 protected
 public
   FSize: integer;
   FSenderName: string;
   procedure DoSynchronize; override;
   constructor Create(AThread: TIdThread; aSize: integer; senderName:string);
   class procedure Add(AThread: TIdThread; aSize: integer; senderName:string);
 end;

implementation

{ TStartFileReceive }

class procedure TStartFileReceive.Add(AThread: TIdThread; aSize: integer; senderName:string);
begin
 with TStartFileReceive.Create(AThread, aSize, senderName) do begin
   try
     Synchronize;
   finally
     Free;
   end;
 end;
end;

constructor TStartFileReceive.Create(AThread:TIdThread; aSize:integer; senderName:string);
begin
 inherited Create(AThread);
 FSize:=aSize;
 FsenderName:=senderName;
end;

procedure TStartFileReceive.DoSynchronize;
var
 fileForm: TfileReceiveForm;
 msgForm: TamcForm;
begin
 msgForm:=mainForm.getBuddyForm(FsenderName,false);
 if msgForm<>nil then begin
   fileForm:=msgForm.fileReceiveForm;
   if fileForm<>nil then begin
     fileForm.DrawBar(4,inttostr(FSize));
     fileForm.receiveState:=sReceiving;
     fileForm.DrawBar(1,"receiving...");
     fileForm.btDir.Enabled:=false;
     fileForm.ProgressBar1.Max:=FSize;
   end;
 end;
end;


чтобы все это дело сработало, я в OnExecute делаю
TStartFileReceive.Add(AThread, aFilesize, senderName);

в твоем случае это будет что-то типа
 TDebugLog = class(TIdSync)
 protected
 public
   FLine: string;
   procedure DoSynchronize; override;
   constructor Create(AThread: TIdThread; aLine: string);
   class procedure Add(AThread: TIdThread; aLine: string);
 end;


**** Использование PostMessage ****
type
 TFileParams = record
   fileName: string;
   fileSize: integer;
   md5hash_server: string;
   md5hash_local: string;
 end;
 PFileParams = ^TFileParams;
 
 TsfSendForm = class(TForm)
   ...
 private
   ...
   procedure DoFileSendStart(recAddr: integer); message WM_SEND_START;
   ...
 public
   ...
 end;

const
 WM_SEND_START       = WM_USER + 1;

implementation

procedure TsfSendForm.DoFileSendStart(recAddr: integer);
var
 fileParams: PFileParams;
 fileItem: TListItem;
 i: integer;
begin
 fileParams:=PFileParams(recAddr);
 lbFile.Caption:=fileParams.fileName;

 fileItem:=TListItem.Create(lvFiles.Items);
 for i:=0 to 3 do fileItem.SubItems.Add("");
 fileItem.SubItems[0]:=inttostr(fileParams.fileSize);
 fileItem.SubItems[1]:=fileParams.md5hash_local;
 fileItem:=lvFiles.Items.AddItem(fileItem);
 fileItem.Caption:=fileParams.fileName;

 ProgressBar1.Max:=fileParams.fileSize;
 ProgressBar1.Position:=0;
 DrawBar(1,"sending...");
 DrawBar(2,"0%");
 DrawBar(3,inttostr(0));
 DrawBar(4,inttostr(fileParams.fileSize));
 Dispose(fileParams);
end;


вызов DoFileSendStart (для этого надо иметь под рукой хэндл окна, которому посылаешь сообщение):

var
 fileParams: PFileParams;
...
 New(fileParams);
 fileParams.fileName:=Copy(FFileName,Length(FLocalDir)+2,999999);
 fileParams.fileSize:=FFileSize;
 fileParams.md5hash_local:=Fmd5hash;
 PostMessage(TsfSendForm(FParentForm).Handle,WM_SEND_START,integer(fileParams),0);


В общем Welcome to Threaded World!


 
alienserg   (2004-07-07 19:27) [10]

еще надо добавить
uses IdSync;


 
Ozone ©   (2004-07-08 12:39) [11]

alienserg   (07.07.04 19:11) [9]

Решил заюзать работу IdSync по вашему примеру.


чтобы все это дело сработало, я в OnExecute делаю
TStartFileReceive.Add(AThread, aFilesize, senderName);

1. А надо ли его выгружать? Если да, то где?

procedure TStartFileReceive.DoSynchronize;
var
fileForm: TfileReceiveForm;
msgForm: TamcForm;
begin
msgForm:=mainForm.getBuddyForm(FsenderName,false);
...


2. Зачем так делать? Нельзя ли просто на прямую обращаться к форме?

3. Что лучше IdSync или PostMessage?


 
alienserg   (2004-07-08 13:11) [12]

Ozone ©   (08.07.04 12:39) [11]
1. А надо ли его выгружать? Если да, то где?
Не надо.
Add это классовый метод, который создает экpемпляр потомка TIdSync, вызывает его Synchronize и потом делает Free.
  try
    Synchronize;
  finally
    Free;
  end;


2. Зачем так делать? Нельзя ли просто на прямую обращаться к форме?
Конечно можно напрямую.
Я просто выдрал кусок из моего приложения. В моем случае я создаю формы динамически, как ICQ создает переговорные формы. Поэтому я и выбираю нужную мне форму таким образом:
msgForm:=mainForm.getBuddyForm(FsenderName,false);

3. Что лучше IdSync или PostMessage?
Я применяю и то и другое. IdSync сделали специально для синхронизации отрисовки на форме из треда TIdTcpServer и его потомков.
PostMessage более гибкий. Я его использую например когда создаю отдельный поток с TIdTcpClient чтобы отрисовывать прогресс передачи файла.
Если используешь PostMessage, заботься чтобы WM_USER + YOUR_CUSTOM_MESSAGE_OFFSET не пересеклось ненароком с каким-нибудь другим подобным идентификатором в пользовательской зоне. Например я использую ThemeEngine и они также используют WM_USER + MESSAGE_OFFSET Предварительно порылся в их коде и убедился, что значения не пересекаются.


 
Ozone ©   (2004-07-08 13:21) [13]

alienserg   (08.07.04 13:11) [12]

Т.е. если я буду юзать WM_USER + 1, WM_USER + 2 и т.д., то я могу кому-то "навредить"?


 
Ozone ©   (2004-07-08 13:24) [14]

И еще, когда та так делаешь

var
fileParams: PFileParams;
...
New(fileParams);
...
PostMessage(TsfSendForm(FParentForm).Handle,WM_SEND_START,integer(fileParams),0)


То в какой момент можно будет выгрузить fileParams. Ведь если это сделать сразу, то в приемнике мы ничего не получим (не успеем)?


 
Ozone ©   (2004-07-09 05:30) [15]

UP


 
Digitman ©   (2004-07-09 08:10) [16]


> Ozone ©   (09.07.04 05:30) [15]


на стороне приемника в этом случае :

var
 fileparams: PFileParams;
..
 fileparams := PFileParams(Message.wParam); //прием параметра
... //обработка параметра
 Dispose(fileparams); //освобождение памяти


 
Ozone ©   (2004-07-09 08:39) [17]

Digitman ©   (09.07.04 08:10) [16]

Спасибо.



Страницы: 1 вся ветка

Форум: "Сети";
Текущий архив: 2004.09.12;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.51 MB
Время: 0.043 c
3-1092716386
leonidus
2004-08-17 08:19
2004.09.12
Не полное отображение содержимого таблицы


3-1092745686
MadGhost
2004-08-17 16:28
2004.09.12
DBGrid multiselect как узнать список который выбрали?


14-1093312846
Думкин
2004-08-24 06:00
2004.09.12
С днем рождения! 24 августа


4-1089994930
Ded Moroz
2004-07-16 20:22
2004.09.12
проблемы с RAS


1-1093340465
oleg_SYS
2004-08-24 13:41
2004.09.12
Как перехватить и изменить событие клавиатуры?





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский