繁体   English   中英

TIdTCPServer如何在60Hz计时器中多播到所有客户端?

[英]How did TIdTCPServer multicast to ALL Clients in A 60Hz timer?

我是Indy的新手。 这是我第一次在这里发布问题。

我的项目必须以60Hz的频率向所有客户端发送数据。 我为此使用TIdTCPServer并监视保持活动状态。 我的工具很旧,可以在WinXP上运行,使用C ++ Builder 6和Indy8。存在潜在的超时问题,是否有人想到如何处理?

这是我的代码:

服务器端

typedef struct
{
    AnsiString PeerIP;     //{ Cleint IP address }
    AnsiString HostName;   //{ Hostname }
    int Id;        // {Cleint ID}
} TClient;


// This is Multimedia timer callback function, on 60Hz

void CALLBACK mmTimerProc(UINT wTimerID, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
{
    DWORD T1, T2;
    TfmMain *pMain = (TfmMain *)dwUser;
    int Index;
    double dT;
    TClient *pClient;

    if (pMain->IsClosing) return;


    if (pMain->Shutdown)
    {
        return;
    }


    pMain->UpdateServer1Data();

    TList *pList = pMain->IdTCPServer1->Threads->LockList();
    try
    {
        for(int X = 0; X < pList->Count; X++)
        {
            if (!pMain->IsClosing)
            {
                TIdPeerThread *AThread = (TIdPeerThread *)pList->Items[X];

                if(AThread != NULL)
                {
                    pClient = (TClient *)AThread->Data;
                    try
                    {
                        if(!AThread->Connection->ClosedGracefully)
                        {
                            // Send data to ALL Clients
                            AThread->Connection->WriteBuffer(&pMain->Data2Client, sizeof(pMain->Data2Client), true);
                        }
                    }
                    catch(Exception &E)
                    {
                        if(!AThread->Stopped)
                        {
                            AThread->Stop();
                            AThread->Connection->Disconnect();
                        }
                    }
                }
            }
        }
    }
    __finally
    {
        pMain->IdTCPServer1->Threads->UnlockList();
    }

    // Shutdown computer or close application

    if(pMain->SimIos.Status.iSimStatus == 11)
    {
        pMain->Shutdown = true;
        pMain->CloseApp();
    }
}


void __fastcall TfmMain::IdTCPServer1Connect(TIdPeerThread *AThread)
{
    TClient *pClient = NULL;
    AnsiString ABuffer, text;
    AnsiString PeerIP   = AThread->Connection->Binding->PeerIP;
    TDateTime TimeConnected = Now();


    ABuffer = AThread->Connection->ReadLn();
    if((ABuffer.Pos("TT") == 0) && (ABuffer.Pos("IG") == 0) && (ABuffer.Pos("RR") == 0))
    {
        text = AnsiString().sprintf("1>>> Unknown(%s) on %s connected illegal!...",
                            PeerIP, DateTimeToStr(TimeConnected));
        WriteMsg(text);
        AThread->Connection->Disconnect();
        return;
    }

    if(ABuffer.Pos("IG") != 0)
    {
        pClient = new TClient;
        pClient->PeerIP     = PeerIP;
        pClient->HostName   = Clients[eIG];
        pClient->Id         = eIG;

        AThread->Data = (TObject *)pClient;
        AThread->FreeOnTerminate = true;

        // Report client is on line
    }

    text = AnsiString().sprintf("1>>>%s(%s) on %s on line!...",
                        pClient->HostName, PeerIP, DateTimeToStr(TimeConnected));
    WriteMsg(text);    
}
//---------------------------------------------------------------------------

void __fastcall TfmMain::IdTCPServer1Disconnect(TIdPeerThread *AThread)
{
    TClient *pClient = NULL;
    AnsiString Msg;


    if (IsClosing) return;


    pClient = (TClient *)AThread->Data;

    if(pClient->Id == 1)
    {
        // Report client is off line
        Msg = AnsiString().sprintf("1>>>%s(%s) on %s off line...",
        pClient->HostName, pClient->PeerIP, DateTimeToStr(Now()));
        WriteMsg(Msg);
    }


    delete pClient;
    AThread->Data = NULL;
    AThread->Terminate();

    try
    {
        IdTCPServer1->Threads->LockList()->Remove(AThread);
    }
    __finally
    {
        IdTCPServer1->Threads->UnlockList();
    }

}
//---------------------------------------------------------------------------

void __fastcall TfmMain::IdTCPServer1Execute(TIdPeerThread *AThread)
{
    TClient *pClient;


    if (!AThread->Terminated && !IsClosing)
    {
        pClient = (TClient *)AThread->Data;
        if((pClient != NULL) && (pClient->Id != 0))
        {
            try
            {
                if(pClient->Id == 1)
                {
                    // Report client still alive
                }
            }
            catch(Exception &E)
            {
                if (!IsClosing)
                {
                    if(!AThread->Stopped)
                    {
                        AThread->Stop();
                        AThread->Connection->Disconnect();
                    }
                }
            }
        }
    }
}

客户端

void __fastcall TSocketThread::Execute()
{
    unsigned long ulCheckSum;
    SIM_SVR1_ACK_STRUCT   Ack2Svr;
    SIM_SVR_DATA   DataFromSvr;
    int Counter;


    memset(&DataFromSvr, 0, sizeof(DataFromSvr));
    memset(&Ack2Svr,  0, sizeof(Ack2Svr));
    Counter = 0;

    // fetch and process commands until the connection or thread is terminated
    while (!this->Terminated && FSocket->Connected())
    {
        try
        {
            // recieve data from server
            FSocket->ReadBuffer(&DataFromSvr, sizeof(DataFromSvr));

            // check CRC
            ulCheckSum = CRC_32((unsigned char*)&DataFromSvr.SimData, sizeof(DataFromSvr.SimData));

            if (ulCheckSum == DataFromSvr.uiCheckSum)
            {
                FSmIpcUtil->Writeto(&DataFromSvr.SimData, DATA_OFFSET, sizeof(DataFromSvr.SimData));
            }

            else
            {
                // counter to record error  
                Synchronize(UpdateCaption);
            }

            // read return from local SM
            FSmIpcUtil->Readfrom(&Ack2Svr, ACK_OFFSET, sizeof(Ack2Svr));

            FSocket->WriteBuffer(&Ack2Svr, sizeof(Ack2Svr));

            if (DataFromSvr.SimData.SimIgTgt.Acdata.iSimStatus == 11)
            {
                Terminate();
                FSocket->Disconnect();

                PostMessage(Application->Handle, WM_SHUTDOWN, 0, 0);

                Sleep(500);
            }
        }

        catch (EIdException& E)
        {
            this->Terminate();
            FSocket->Disconnect();
        }
    }
}

您的代码有几个问题。

多媒体计时器回调在允许执行的操作中受到严格限制:

应用程序不应从回调函数内部调用任何系统定义的函数, 除了 PostMessagetimeGetSystemTimetimeGetTimetimeSetEventtimeKillEventmidiOutShortMsgmidiOutLongMsgOutputDebugString

如果传输速度很重要,则完全不用计时器回调来进行广播。 将数据保存在安全的地方,然后让每个TIdTCPServer线程根据自己的时间获取最新数据。 这也使每个连接线程保持隔离,因此如果发生问题,一个连接不会影响任何其他连接。

不要将TIdPeerThread::FreeOnTerminate设置为true,不要调用TIdPeerThread::Stop()TIdTCPServer::Threads要从TIdTCPServer::Threads属性中手动删除线程。 您不拥有线程,而TIdTCPServer拥有线程, TIdTCPServer为您管理线程。 如果要停止给定的线程,只需要做的就是关闭线程的套接字。 但是,通过将所有发送逻辑OnExecute它所属的OnExecute中,可以让TIdTCPServer处理任何错误并为您关闭套接字。

仅当IG客户端连接时,您的OnConnect事件处理程序才设置AThread->Data ,但您的OnConnectOnDisconnect处理程序在尝试访问TClient对象之前不会检查该条件。

如果IsClosing为true,则您的OnDisconnect事件处理程序正在泄漏内存。 它正在调用Threads->UnlockList()而不先调用Threads->LockList() 如果列表未被调用线程锁定,则尝试对其进行解锁会导致错误和死锁。

尝试更多类似这样的方法:

struct TClient
{
    AnsiString PeerIP;     //{ Client IP address }
    AnsiString HostName;   //{ Hostname }
    int Id;                //{ Client ID }
};

void CALLBACK mmTimerProc(UINT wTimerID, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
{
    TfmMain *pMain = (TfmMain *)dwUser;

    if (pMain->IsClosing || pMain->Shutdown) return;

    pMain->UpdateServer1Data();    
    // make sure pMain->Data2Client is thread-safe...
    // set a signal that Data2Client has been updated...

    // Shutdown computer or close application

    if (pMain->SimIos.Status.iSimStatus == 11)
    {
        pMain->Shutdown = true;
        pMain->CloseApp();
    }
}

void __fastcall TfmMain::IdTCPServer1Connect(TIdPeerThread *AThread)
{
    TClient *pClient;
    AnsiString ABuffer, text;
    AnsiString PeerIP = AThread->Connection->Binding->PeerIP;
    TDateTime TimeConnected = Now();

    ABuffer = AThread->Connection->ReadLn();
    if ((ABuffer.Pos("TT") == 0) && (ABuffer.Pos("IG") == 0) && (ABuffer.Pos("RR") == 0))
    {
        text = AnsiString().sprintf("1>>> Unknown(%s) on %s connected illegal!...", PeerIP.c_str(), DateTimeToStr(TimeConnected).c_str());
        WriteMsg(text);
        AThread->Connection->Disconnect();
        return;
    }

    pClient = new TClient;
    pClient->PeerIP = PeerIP;

    if (ABuffer.Pos("IG") != 0)
    {
        pClient->HostName   = Clients[eIG];
        pClient->Id         = eIG;
    }
    else
        pClient->Id         = 0;

    AThread->Data = (TObject *)pClient;

    // Report client is on line

    text = AnsiString().sprintf("1>>>%s(%s) on %s on line!...", pClient->HostName.c_str(), PeerIP.c_str(), DateTimeToStr(TimeConnected).c_str());
    WriteMsg(text);    
}

void __fastcall TfmMain::IdTCPServer1Disconnect(TIdPeerThread *AThread)
{
    TClient *pClient = (TClient *)AThread->Data;
    AnsiString Msg;

    AThread->Data = NULL;

    if (pClient)
    {
        // Report client is off line
        Msg = AnsiString().sprintf("1>>>%s(%s) on %s off line...",
            pClient->HostName.c_str(), pClient->PeerIP.c_str(), DateTimeToStr(Now()).c_str());
        WriteMsg(Msg);

        delete pClient;
    }
}

void __fastcall TfmMain::IdTCPServer1Execute(TIdPeerThread *AThread)
{
    TClient *pClient;

    if (IsClosing) return;

    // make sure pMain->Data2Client is thread-safe...
    if (Data2Client has been updated since last event)
    {
        AThread->Connection->WriteBuffer(&pMain->Data2Client, sizeof(pMain->Data2Client), true);
    }

    pClient = (TClient *)AThread->Data;
    // Report client still alive
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM