我是个和印第一起工作的新手。这是我第一次在这里发布问题。
我的项目必须以60Hz的频率向所有客户发送数据。我正在使用TIdTCPServer来进行此操作,并监视keep alives。我的工具很旧,运行在WiXP上,使用C++ Builder 6和Dyy 8。有一个潜在的超时问题,有没有人会好好考虑如何处理它?
这是我的代码:
服务器端

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属性中手动删除线程。您不拥有线程,TIdTCPServerdo,它将为您管理它们。如果要停止给定的线程,只需关闭该线程的套接字。但是,通过将所有发送逻辑移到它所属的OnExecute中,您可以让TIdTCPServer处理任何错误并为您关闭套接字。
只有当一个ig客户机连接时,OnConnect事件处理程序才设置为AThread->Data,但在尝试访问OnConnect对象之前,您的OnDisconnectTClient处理程序没有检查该条件。
如果OnDisconnect为真,IsClosing事件处理程序正在泄漏内存。它先调用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
}

关于delphi - TIdTCPServer如何在60Hz定时器中多播到所有客户端?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/40519135/

10-10 17:14