2

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

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

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

但是,当服务器尝试向断开连接的客户端写入数据时……Write() 或 WriteLn() 在尝试写入时挂起,就像在等待某种写入超时。这会挂起空洞数据包分发过程,从而在向所有其他客户端的数据传输中产生延迟。几秒钟后,“套接字连接已关闭”异常被引发并且数据流继续。

这是代码

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 缓冲区中,客户端线程不断检查可用数据并挑选并将其传递给自己?这样,如果一个线程挂起,它将不会停止/延迟整个分发线程。

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

问候

果酱

4

1 回答 1

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); 
} 
于 2010-04-14T20:24:51.020 回答