Завершение TCP-сервера Indy10
Я использовал Indy с C++ Builder XE3. Это идеальная система, но у меня есть некоторые проблемы. IdTCPServer работает очень хорошо, но когда у меня есть несколько подключений к нему, и я хочу остановить сервер, мое приложение зависло. Я пытаюсь рассказать, как я делаю это шаг за шагом: 1) Запуск приложения (и прослушивание сервера) 2) Ожидание новых подключений (или имитация, без разницы) 3) Когда у нас есть 10-15 подключений - затем попытайтесь остановить сервер прослушивания. 4) когда код пришел к IdTCPServer1->Active = false - приложение будет заморожено
Я сделал небольшое видео. Может быть, это объясняет ситуацию намного лучше. http://www.youtube.com/watch?v=BNgTxYbLx8g
И вот мой код:
OnConnect:
EnterCriticalSection(&CritLock);
++ActiveConnections;
SetActiveConnections(ActiveConnections);
LeaveCriticalSection(&CritLock);
OnDisconnect:
EnterCriticalSection(&CritLock);
--ActiveConnections;
SetActiveConnections(ActiveConnections);
LeaveCriticalSection(&CritLock);
Код StopServer:
void TForm1::StopServer()
{
TList *list = IdTCPServer1->Contexts->LockList();
try
{
for(int i = 0; i < list->Count; ++i)
{
TIdContext *AContext = reinterpret_cast<TIdContext*>(list->Items[i]);
try
{
if (AContext->Connection->Connected())
{
AContext->Connection->IOHandler->InputBuffer->Clear();
AContext->Connection->IOHandler->WriteBufferCancel();
AContext->Connection->IOHandler->WriteBufferClear();
AContext->Connection->IOHandler->WriteBufferClose();
AContext->Connection->IOHandler->CloseGracefully();
AContext->Connection->Disconnect();
}
}
catch (const Exception &e)
{
}
}
}
__finally
{
IdTCPServer1->Contexts->UnlockList();
}
IdTCPServer1->Contexts->Clear();
//IdTCPServer1->StopListening();
IdTCPServer1->Active = false;
}
Спасибо за совет!
1 ответ
Вам нужно избавиться от всех ваших StopServer()
код, за исключением самой последней строки. когда TIdTCPServer
деактивирован, он выполняет все необходимые очистки для вас. НЕ ДЕЛАЙТЕ ЭТОГО СЕБЯ (тем более, что вы все равно делаете это неправильно).
void TForm1::StopServer()
{
IdTCPServer1->Active = false;
}
Теперь, имея только этот код, если ваше приложение все еще зависает, это означает, что вы блокируете основной поток. Это происходит, если вы звоните StopServer()
в контексте основного потока и одна из двух вещей происходит в коде вашего сервера:
один из ваших
TIdTCPServer
Обработчики событий выполняют синхронизированную операцию с основным потоком (либо черезTIdSync
или жеTThread::Synchronize()
).один из ваших
TIdTCPServer
обработчики событий проглатывает Indy исключения и не позволяетTIdTCPServer
правильно завершить один или несколько клиентских потоков при необходимости.
Внутренне TIdTCPServer::Active
установщик свойств закрывает все активные сокеты и ожидает полного завершения их соответствующих потоков, блокируя вызывающий поток до выхода из установщика свойств. Если вы деактивируете сервер в главном потоке, и один из потоков сервера выполняет синхронизацию, которую основной поток не может обработать, или иначе не завершается корректно, когда это должно быть, это заблокирует деактивацию сервера от выхода и, таким образом, заблокирует основной нить.
Поэтому убедитесь, что:
вы не выполняете операции синхронизации с основным потоком, пока сервер отключается основным потоком. Если вам необходимо выполнить синхронизацию, то деактивируйте сервер в рабочем потоке, чтобы основной поток больше не блокировался.
ваши обработчики событий не глотают инди
EIdException
исключения вtry/catch
блоки. Если вы поймете такое исключение, перебросьте его, когда вы закончите, используя его. ПозволятьTIdTCPServer
обрабатывать любые исключения Indy, чтобы он мог выполнять внутреннюю очистку по мере необходимости.
Наконец, отметим, что вам не нужно отслеживать соединения вручную. TIdTCPServer
уже делает это для вас в Contexts
имущество. Если вам нужно знать, сколько клиентов в данный момент подключено, просто Lock()
Contexts
список, прочитайте его Count
собственности (или делать что-то еще, что вам нужно сделать с клиентами), а затем Unlock()
список.