【问题标题】:Abnormally disconnected TCP sockets and write timeoutTCP 套接字异常断开和写入超时
【发布时间】:2011-01-30 03:18:48
【问题描述】:

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

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

所以现在我们有一个断开连接的套接字,但正如您所知,服务器端尚未检测到它,因此服务器也会继续尝试向该客户端发送数据。

但是当服务器尝试向断开连接的客户端写入数据时...... 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 秒不活动后断开套接字。但是你可以想象,这个机制仍然不能与这个广播循环完全同步,因为这个广播循环几乎一直在运行。

那么我可以通过 iohandler 或其他方式指定任何写入超时吗?我已经看到很多关于“检测断开的 tcp 套接字”的线程,但我的问题略有不同,我需要在写入尝试期间避免挂断几秒钟。

那么有什么解决办法吗?

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

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

问候

卡纸

【问题讨论】:

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


    【解决方案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); 
    } 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-05-12
      • 1970-01-01
      • 2016-08-01
      • 2013-05-25
      • 2019-07-11
      • 1970-01-01
      • 2011-11-01
      • 2012-11-25
      相关资源
      最近更新 更多