Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

TIdTCPServer: Восстановление коннекта [D7, WinXP]


Сатир ©   (2007-06-12 16:16:00) 

Есть шлюз, который обговаривался в этих ветках:
http://www.delphimaster.ru/cgi-bin/forum.pl?id=1179906322&n=3
http://www.delphimaster.ru/cgi-bin/forum.pl?id=1180947379&n=3
Произвел переход на Indy 10.
Также произвел легкий рефакторинг кода, разделив на два независимых сервиса.
Но главная проблемма так и осталась: после перезагрузки удалённой системы сабж не видит текущий коннект и считает, что связи нету.
А теперь подробней, как это выглядит в коде:
есть такая структура для хранения активных коннектов

Type
 PClient = ^TClient;
 TClient = Record // Object holding data of client (see events)
   DNS: String[20]; { Hostname }
   Connected, { Time of connect }
   LastAction: TDateTime; { Time of last transaction }
   Thread: Pointer; { Pointer to thread }
   Context: TIdContext;
 End;

есть
переменная класса Clients:TThreadList, в которой хранятся указатели на активные коннекты
Заполнение этой структуры происходит в методе idTCPServer.OnConnect
Procedure TSentDM.IdTCPServerConnect2(AContext: TIdContext {AThread: TIdPeerThread});
Var
 NewClient: PClient;
Begin
 Try
   TidThread(AContext.Yarn).Synchronize(ShowServerConnect);

   GetMem(NewClient, SizeOf(TClient));
   NewClient.DNS := GetCompName {IdTCPServer.LocalName};
   NewClient.Connected := Now;
   NewClient.LastAction := NewClient.Connected;
   NewClient.Thread := AContext.Yarn;
   // NewClient.Connection := AContext.Connection;
   NewClient.Context := AContext;

   AContext.Data := TObject(NewClient);

   Try
     Clients{IdTCPServer.Contexts}.LockList.Add(NewClient);
   Finally
     Clients{IdTCPServer.Contexts}.UnlockList;
   End;
   
 Except
   On E: Exception Do
     DM.WriteTMPlog('Error');
 End;
End;


на событии дисконнекта происходит следующее:

Procedure TSentDM.IdTCPServerDisconnect2(AContext: TIdContext {AThread: TIdPeerThread});
Var
 ActClient: PClient;
Begin
 Try
   TIdThread(AContext.Yarn).Synchronize(ShowServerDisConnect);

   ActClient := PClient(AContext.Data);
   SetConnectionStatus(1, 0);
   Try
     Clients{IdTCPServer.Contexts}.LockList.Remove(ActClient);
   Finally
     Clients{IdTCPServer.Contexts}.UnlockList;
     FreeMem(ActClient);
   End;
   AContext.Data := Nil;
except
  //log error
end
end;

и само использование активного коннекта происходит таким образом:

Function TSentDM.SendXMLfile(FilePath: String): TBackMsg;
Var
 FileStrim: TFileStream;
 RecClient: PClient;
 Context: TIdContext;
 i: integer;
 AStream: TIdStreamVCL;
Begin
 Try

   With Clients{IdTCPServer.Contexts}.LockList Do
   Begin
     For i := 0 To Count - 1 Do // iterate through client-list
     Begin
       RecClient := Items[i]; // get client-object

       If Not RecClient.Context.Connection.Connected Then
         Begin
            Вот это не помогает. Используемый контекст считает, что он всё ещё не активен. Как создать новый контекст подключения, чтобы использовать его для пересылки файла?
           IdTCPServer.Active := true;
                 
         End;

       FileStrim := TFileStream.Create(FilePath, fmOpenReadWrite);

       AStream := TIdStreamVCL.Create(FileStrim);
       Try
         FileStrim.Position := 0;
         RecClient.Context.Connection.IOHandler.WriteBufferOpen;
         Try
           RecClient.Context.Connection.IOHandler.Write(AStream);
           Try
             RecClient.Context.Connection.IOHandler.WriteBufferFlush;
           Except
             IdTCPServerDisconnect2(RecClient.Context);
           End;
         Finally
           RecClient.Context.Connection.IOHandler.WriteBufferClose;
         End;

         Result.ID := 1;
         Result.Msg := 'W: Файл успешно отправлен: ' + ExtractFileName(FilePath);
       Finally
         FileStrim.Free;
         //  RecThread.Free;
       End;
     End;
   End;
   If Clients{IdTCPServer.Contexts}.LockList.Count = 0 Then
   Begin
     Result.ID := 0;
     Result.Msg := 'E: Файл: ' + ExtractFileName(FilePath) +
       ' не отправлен так как нет актиных конектов';
   End;
 Except
   On E: Exception Do
   Begin
     Result.ID := -1;
     Result.Msg := 'E: Ошибка процедуры [SendXMLfile]: ' + E.Message;
   End;
 End;
End;


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


Сергей М.   (2007-06-14 08:27:00) [1]

А где обработка потенциальных исключений в обработчике OnDisconnect ?

Что-то я не вижу ее ..

except
// log error <-- здесь должно быть реальное протоколирование исключений
end;

?


Сатир   (2007-06-13 18:02:00) [2]

поменял TThreadList на TIdThreadSafeList.
Но всё равно в методе дисконнекта выдает кучу ошибок...


Сатир   (2007-06-13 17:33:00) [3]

Пробовал заменить TThreadList на обычный TList(у меня только один клиент и возможно только одно соединение), чтобы нормально удалять из него указатели, но оказалось, что это повлекло за собой кучу ошибок в индийском коде в районе метода DoDisconnect
На что ещё можно заменить TThreadList и возможно ли сделать соединение без использования потоков?


Сатир   (2007-06-13 17:01:00) [4]


> Может быть при ее выполнении ?

да-да, верно. При её выполнении, передаётся управление программе.
То есть, я стою на строке Remove, нажимаю F8 и программа молчаливо продолжает работать, хотя должна остановиться на строке Clear.
Чтобы это могло значить?
Никаких замечаний от среды отладки не поступало...


Сергей М.   (2007-06-13 16:55:00) [5]


> после этой строки


Что значит "после" ?

Может быть приее выполнении ?

Изволь выражаться точнее ..

Потому что "после" - это строка с Clear ...


Сатир   (2007-06-13 16:47:00) [6]


> Значит возникло исключение.


> При этом, никакого сообщение об ошибке не возникло.

В отладчике стоит птица "Stop on Delphi Exceptions"


Сергей М.   (2007-06-13 16:38:00) [7]


> отладчик передал управление программе и не перешёл на следующую
> строку


Значит возникло исключение.


Сатир   (2007-06-13 15:59:00) [8]

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

> Try
>      Clients{IdTCPServer.Contexts}.LockList.Remove(ActClient);
>
>    Finally
>      Clients{IdTCPServer.Contexts}.UnlockList;
>      FreeMem(ActClient);
>    End;

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

Try
     Clients.LockList.Remove(ActClient);
     Clients.LockList.Clear;
   Finally
     Clients.UnlockList;
     FreeMem(ActClient);
   End;
 

но когда прошёлся под отладчиком по этому куску, после этой строки
     Clients.LockList.Remove(ActClient);
отладчик передал управление программе и не перешёл на следующую строку.
При этом, никакого сообщение об ошибке не возникло.
Посему, у меня возникло предположение, что неактивный контекст не был удален из списка активных контекстов.


Сергей М.   (2007-06-13 15:21:00) [9]

Так..

И ты утверждаешь, что в момент вызова SendXMLfile в твоем списке Clients все его элементы ссылаются на контексты not-connected-соединений ?


Сатир   (2007-06-13 15:02:00) [10]


> Т.е. метод SendXMLfile вызывается в теле обработчика таймера
> ?

в теле обработчика вызывается метод, который выполняет пакет заданий, в том числе и SendXMLfile


Сергей М.   (2007-06-13 13:37:00) [11]

Т.е. метод SendXMLfile вызывается в теле обработчика таймера ?


Сатир   (2007-06-13 13:20:00) [12]


> Прямо в обработчике OnConnect ?

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


Сергей М.   (2007-06-13 12:38:00) [13]


> и после этого


Что значит "после этого" ?
Прямо в обработчике OnConnect ?


Сатир   (2007-06-13 12:23:00) [14]


> Новое соединение - это новый контекст, не имеющий отношения
> к контексту прежнего, уже несуществующего соединения.

Объясняю проблемму:
Самое первое соединение - Context1 добавили в Clients, всё нормально работает.
Потом клиент перегружается, происходит дисконнект, ссылка на первый контекст из Clients удаляется
вот код, который это делает:
Clients.LockList.Remove(ActClient);
потом происходит второе соединение - Context2, после того как клиент заново загрузился. Добавляем его в список Clients
Clients.LockList.Add(NewClient);
и после этого пытаемся его использовать для отправки файлов
но оказывается, что он не активен, потому что зрабатывает это условие
If Not RecClient.Context.Connection.Connected Then

Где логика?


Сергей М.   (2007-06-13 10:50:00) [15]


> не даёт новому стать на его место


И не даст.

Новое соединение - это новый контекст, не имеющий отношения к контексту прежнего, уже несуществующего соединения.


Сатир   (2007-06-13 10:31:00) [16]


> Инициатором соединения является клиент, а не сервер.

Вот он(клиент) его и пересоздаёт после своей перегрузки.
Но сервер держит старый контекст и не даёт новому стать на его место.


Сергей М.   (2007-06-13 08:48:00) [17]


> как пересоздать контекст соединения


А никак.
Инициатором соединения является клиент, а не сервер.


Наверх

Титульная страница Поиск, карта сайта Написать письмо