Если клиент получает ошибку "Connection closed gracefully", это означает, что сервер закрыл соединение с клиентом с серверной стороны. Если в коде сервера не было явно инициировано закрытие соединения, то, скорее всего, произошло возникновение необработанного исключения в одном из обработчиков событий сервера. Это может привести к закрытию сокета сервером, если исключение возникло после события OnConnect и до события OnDisconnect, при этом OnDisconnect срабатывает перед закрытием сокета. В компоненте TIdTCPServer есть событие OnException, которое предназначено для отображения такой ситуации.
Компонент TIdTCPClient закрывает сокет во время уничтожения, если он ещё открыт.
Обновление: TIdTCPServer - это многопоточный компонент, и каждое соединение с клиентом выполняется в отдельном потоке. ADO использует COM-объекты, привязанные к апартаменту потока, в котором они были созданы, и они могут использоваться только в контексте этого потока, если не передать их через границы потоков с помощью функций CoMarshalInterThreadInterfaceInStream() и CoGetInterfaceAndReleaseStream(), или интерфейса IGlobalInterfaceTable.
В данной ситуации рекомендуется:
Обеспечить каждому клиенту свои собственные объекты ADO и запросы. Это можно сделать двумя способами:
A. Создать их в событии OnConnect и хранить внутри TIdContext для использования в событии OnExecute, а затем освободить в OnDisconnect. Или создавать и освобождать по мере необходимости в OnExecute.
B. Произвести наследование класса от TIdThreadWithTask и переопределить виртуальные методы BeforeExecute() и AfterExecute() для создания и освобождения объектов ADO, а затем назначить один из компонентов TIdSchedulerOfThread... свойству TIdTCPServer.Scheduler и ваш класс потока свойству TIdSchedulerOfThread.ThreadClass. В событиях сервера можно использовать TMyThreadClass(TIdYarnOfThread(TIdContext.Yarn).Thread) для доступа к ADO объектам вызывающего потока.
Создать отдельный пул объектов ADO. Когда клиенту требуется доступ к базе данных, необходимо передать соответствующие объекты ADO в контекст вызывающего потока, а затем вернуть их в пул после завершения работы.
В обоих случаях, учитывая, что ADO основан на COM, не забудьте вызвать CoInitialize/Ex() и CoUnintialize() для каждого потока клиента, который требует доступа к объектам ADO, либо в событиях OnConnect и OnDisconnect, либо в методах TIdThreadWithTask.BeforeExecute() и TIdThreadWithTask.AfterExecute().
Пример кода на Object Pascal для создания пула соединений ADO в событии OnConnect может выглядеть так:
procedure TServerIdTCPServer.IdTCPServerConnect(AContext: TIdContext);
var
ADOConnection: TADOConnection;
begin
ADOConnection := TADOConnection.Create(Nil);
try
ADOConnection.ConnectionString := 'Ваша строка соединения';
ADOConnection.Open;
AContext.Connection := ADOConnection;
except
on E: Exception do
// Обработка исключения
end;
end;
И в событии OnDisconnect:
procedure TServerIdTCPServer.IdTCPServerDisconnect(AContext: TIdContext);
begin
with AContext.Connection do
begin
if State in [bsConnected, bsConnecting] then
Close;
Free;
end;
end;
Эти примеры демонстрируют базовый подход к управлению ресурсами ADO в многопоточном сервере на Delphi. Важно помнить о правильной инициализации и освобождении ресурсов, а также о перекрестных ссылках потоков при работе с COM-объектами.
В многопоточных серверах на Delphi при обработке ошибки 'Connection Closed Gracefully' необходимо тщательно управлять ресурсами, такими как объекты ADO, чтобы обеспечить их корректное использование и освобождение в разных потоках.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.