TIdTCPServer Contexts->LockList() 上的应用程序挂断

Application hangup on TIdTCPServer Contexts->LockList()

本文关键字:应用程序 Contexts- gt LockList TIdTCPServer      更新时间:2023-10-16

我正在c++ Builder XE2中开发所谓的"feeder"应用程序,它使用2个内置的Indy组件- TIdTCPClient和TIdTCPSever。TIdTCPClient用于从一个源接收数据,形成字符串消息,然后使用tidtcpserver将此字符串消息发送到所有客户端应用程序。对于数据重新翻译,我使用下一个函数(idEventsServerSocket是TIdTCPSever组件):

void TfrmMainWindow::SendDataToAllClients(String msg) {
TList *ClientsList;
try
{
ClientsList = idEventsServerSocket->Contexts->LockList();

for (int i = 0; i < ClientsList->Count; i++) {
    TIdContext *Context = (TIdContext*)ClientsList->Items[i];

    bool connected = false;
    try {
        connected = Context->Connection->Connected();
    }
    catch (Exception&e) {
        continue;
    }
    if (!connected)
        continue;
    try {
        Context->Connection->IOHandler->WriteLn(msg);
        Context->Connection->IOHandler->WriteBufferFlush();
    }
    catch (Exception&e) {
    }
}
}
__finally
{
idEventsServerSocket->Contexts->UnlockList();
}

}

我还想注意到这个函数包含在EnterCriticalSection中…leaveccriticalsection代码段,因此应该保证在函数未执行之前不会出现对该函数代码的新入口。对于idEventsServerSocket, OnException和OnListenException处理程序被定义并包含空代码。

所以问题是:有时候

这行
ClientsList = idEventsServerSocket->Contexts->LockList();

导致应用程序挂起。当它发生时没有一般的规律,但看起来它发生在函数SendDataToAllClients非常频繁地被调用时(例如每10 - 50毫秒一次)。客户端连接数为30 ~ 50。我想知道的是,有没有办法避免这种僵局?我有任何类型的检查(如TryEnterCriticalSection) ?我也想承认,Remy的解决方案从Delphi: TThreadList有时锁程序没有帮助。

LockList()需要在try块之外。

Contexts列表在内部使用了一个临界区,所以在你自己的临界区中包装这些代码是多余的。

LockList()可以阻塞的唯一方式是如果另一个线程已经获得了锁并且没有释放它,或者因为它正在忙着使用列表,或者更有可能它崩溃并且没有释放锁。

在此代码中不要调用Connected()WriteBufferFlush()。您没有使用写缓冲,并且从TIdTCPServer事件之外调用Connected()将在连接的InpuBuffer上导致竞争条件,干扰管理该连接的服务器线程,这可能导致崩溃、死锁、入站数据损坏等。只需单独调用Write(),并让它在套接字已断开连接时抛出异常。

你所展示的是用TIdTCPServer实现TCP广播的一种不安全的方式。您应该实现每个客户机线程安全的出站队列,并让OnExecute事件处理实际的写入:

#include <IdThreadSafe.hpp>
class TMyContext : public TIdServerContext
{
public:
    TIdThreadSafeStringList *Queue;
    bool HasMsgsInQueue;
    __fastcall TMyContext(TIdTCPConnection *AConnection, TIdYarn *AYarn, TIdContextThreadList *AList = NULL)
        : TIdServerContext(AConnection, AYarn, AList)
    {
        Queue = new TIdThreadSafeStringList;
        HasMsgsInQueue = false;
    }
    __fastcall TMyContext()
    {
        delete Queue;
    }
};
__fastcall TfrmMainWindow::TfrmMainWindow(TComponent *Owner)
    : TForm(Owner)
{
    // set this before activating the server
    idEventsServerSocket->ContextClass = __classid(TMyContext);
}
void TfrmMainWindow::SendDataToAllClients(const String &msg)
{
    TList *ClientsList = idEventsServerSocket->Contexts->LockList();
    try
    {
        for (int i = 0; i < ClientsList->Count; ++i)
        {
            TMyContext *Context = (TMyContext*) ClientsList->Items[i];
            try
            {
                TStringList *Queue = Context->Queue->Lock();
                try
                {
                    Queue->Add(msg);
                    Context->HasMsgsInQueue = true;
                }
                __finally
                {
                    Context->Queue->Unlock();
                }
            }
            catch (const Exception &)
            {
            }
        }
    }
    __finally
    {
        idEventsServerSocket->Contexts->UnlockList();
    }
}
void __fastcall TfrmMainWindow::idEventsServerSocketExecute(TIdContext *AContext)
{
    TMyContext *ctx = (TMyContext*) AContext;
    if (ctx->HasMsgsInQueue)
    {
        TStringList *Msgs = NULL;
        try
        {
            TStringList *Queue = ctx->Queue->Lock();
            try
            {
                Msgs = new TStringList;
                Msgs->Assign(Queue);
                Queue->Clear();
                ctx->HasMsgsInQueue = false;
            }
            __finally
            {
                ctx->Queue->Unlock();
            }
            AContext->Connection->IOHandler->Write(Msgs);
        }
        __finally
        {
            delete Msgs;
        }
    }
    if (AContext->Connection->IOHandler->InputBufferIsEmpty())
    {
        AContext->Connection->IOHandler->CheckForDataOnSource(100);
        AContext->Connection->IOHandler->CheckForDisconnect();
        if (AContext->Connection->IOHandler->InputBufferIsEmpty())
            return;
    }
    // handle inbound data as needed...
}