异常断开的TCP套接字和写入超时

时间:2010-03-21 21:42:54

标签: c++builder indy c++builder-2010

我会尽量用最短的话解释这个问题。我正在使用c ++ builder 2010。

我正在使用TIdTCPServer并将语音数据包发送到已连接的客户端列表。一切正常,直到任何客户端异常断开连接,例如电源故障等。我可以通过切断已连接客户端的以太网连接来重现类似的断开连接。

所以现在我们有一个断开连接的套接字但是你知道它还没有在服务器端检测到,所以服务器也会继续尝试向该客户端发送数据。

但是当服务器尝试将数据写入该断开连接的客户端时...... Write()或WriteLn()HANGS在尝试写入时,就像它正在为某些写入超时而烦恼。这会导致漏洞数据包分发过程中断,从而导致向所有其他客户端传输数据的延迟。几秒钟后“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循环完全同步,因为这个braodcasting循环几乎一直在运行。

那么我可以指定的任何写入超时可能是通过iohandler或其他东西吗?我见过许多关于“检测断开连接的tcp套接字”的线程,但我的问题没什么不同,我需要在写入尝试期间避免挂断几秒钟。

那有什么解决方案吗?

或者我应该考虑使用某种不同的机制进行此类数据广播,例如广播循环将数据包放入某种FIFO缓冲区,客户端线程不断检查可用数据并选择并将其传递给自己?这样,如果一个线程挂起,它将不会停止/延迟整个分发线程。

有什么想法吗?感谢您的时间和帮助。

此致

卡纸

1 个答案:

答案 0 :(得分:1)

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); 
}