Неправильно отключенные TCP-сокеты и тайм-аут записи - PullRequest
1 голос
/ 22 марта 2010

Я постараюсь объяснить проблему в кратчайших словах. Я использую C ++ Builder 2010.

Я использую TIdTCPServer и отправляю голосовые пакеты в список подключенных клиентов. Все работает нормально, пока любой клиент не будет отключен ненормально, например, сбой питания и т. Д. Подобное отключение можно воспроизвести, обрезав соединение Ethernet подключенного клиента.

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

Но когда сервер пытается записать данные в этот отключенный клиент ...... Write () или WriteLn () вешает туда при попытке записи, это похоже на ожидание некоторого тайм-аута записи. Это приводит к зависанию процесса распределения закрытых пакетов, что приводит к задержке передачи данных всем остальным клиентам. Через несколько секунд выдается исключение «Socket Connection Closed» и поток данных продолжается.

Вот код

try
{
EnterCriticalSection(&SlotListenersCriticalSection);
for(int i=0;i<SlotListeners->Count;i++)
 {
    try
    {

      //Here the process will HANG for several seconds on a disconnected socket
      ((TIdContext*) SlotListeners->Objects[i])->Connection->IOHandler->WriteLn("Some DATA");

   }catch(Exception &e)
   {
     SlotListeners->Delete(i);
   }
}
}__finally
{
 LeaveCriticalSection(&SlotListenersCriticalSection);
}

Хорошо, у меня уже есть механизм поддержки активности, который отключает сокет после n секунд бездействия. Но, как вы можете себе представить, этот механизм не может синхронизироваться точно с этим циклом braodcasting, потому что этот цикл braodcast работает почти все время.

Так есть ли тайм-ауты записи, которые я могу указать, может быть через iohandler или что-то? Я видел много-много тем о «Обнаружении отсоединенного TCP-сокета», но моя проблема немного отличается, мне нужно избегать этого зависания на несколько секунд во время попытки записи.

Так есть ли решение?

Или я должен рассмотреть возможность использования какого-то другого механизма для такой трансляции данных, например, цикл широковещания помещает пакет данных в некоторый буфер FIFO, а клиентские потоки постоянно проверяют доступные данные и выбирают и доставляют их себе? Таким образом, если один поток зависнет, он не остановит / не задержит весь поток распространения.

Есть идеи, пожалуйста? Спасибо за ваше время и помощь.

Привет

Джемы

1 Ответ

1 голос
/ 15 апреля 2010

В Indy нет таймаутов записи.Для этого вам придется использовать метод TIdSocketHandle.SetSockOpt () для непосредственной установки тайм-аутов на уровне сокетов.

Буфер FIFO - лучший вариант (и вообще лучший дизайн).Например:

void __fastcall TForm1::IdTCPServer1Connect(TIdContext *AContext)
{
    ...
    AContext->Data = new TIdThreadSafeStringList;
    ...
}

void __fastcall TForm1::IdTCPServer1Disconnect(TIdContext *AContext)
{
    ...
    delete AContext->Data;
    AContext->Data = NULL;
    ...
}

void __fastcall TForm1::IdTCPServer1Execute(TIdContext *AContext)
{
    TIdThreadSafeStringList *Queue = (TIdThreadSafeStringList*) AContext->Data;
    TStringList *Outbound = NULL;
    TStringList *List = Queue->Lock();
    try
    {
        if( List->Count > 0 )
        {
            Outbound = new TStringList;
            Outbound->Assign(List);
            List->Clear();
        }
    }
    __finally
    {
        Queue->Unlock();
    }

    if( Outbound )
    {
        try
        {
            AContext->Connection->IOHandler->Write(Outbound);
        }
        __finally
        {
            delete Outbound;
        }
    }

    ...
}

...

try
{
    EnterCriticalSection(&SlotListenersCriticalSection);
    int i = 0;
    while( i < SlotListeners->Count )
    {
        try
        {
          TIdContext *Ctx = (TIdContext*) SlotListeners->Objects[i];
          TIdThreadSafeStringList *Queue = (TIdThreadSafeStringList*) Ctx->Data;
          Queue->Add("Some DATA"); 
          ++i;
        }
        catch(const Exception &e) 
        { 
            SlotListeners->Delete(i); 
        } 
    } 
}
__finally 
{ 
    LeaveCriticalSection(&SlotListenersCriticalSection); 
} 
...