2016-11-10 35 views
0

私はIndyと働いている初心者です。ここに質問を投稿するのは初めてです。TIdTCPServerは60Hzタイマーですべてのクライアントにどのようにマルチキャストしましたか?

私のプロジェクトは60Hzですべてのクライアントにデータを送信する必要があります。私はこのためにTIdTCPServerを使用しており、キープアライブを監視しています。私のツールはC++ Builder 6とIndy 8を使用してWinXP上で動作する非常に古いものです。潜在的なタイムアウトの問題があります。

サーバー側

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(); 
     } 
    } 
} 

答えて

3

あなたのコードにはいくつかの問題があります。

は、ここに私のコードです。

マルチメディアタイマーコールバック

は非常に実行できる操作が制限されています

アプリケーションは、コールバック関数の中から任意のシステム定義関数を呼び出してはいけません、PostMessagetimeGetSystemTimetimeGetTimeを除くtimeSetEvent,timeKillEvent,midiOutShortMsg,midiOutLongMsgおよびOutputDebugStringである。

転送速度が重要な場合は、タイマーコールバックをブロードキャストしないでください。安全な場所にデータを保存してから、それぞれTIdTCPServerスレッドが独自の時間に最新のデータを取得するようにします。これにより、各接続スレッドが分離されたままになるので、問題が発生した場合でも一方の接続が他の接続に影響することはありません。

TIdPeerThread::FreeOnTerminateをtrueに設定しないでください。TIdPeerThread::Stop()を呼び出しないでください。TIdTCPServer::Threadsプロパティからスレッドを手動で削除しないでください。あなたはスレッドを所有していません、TIdTCPServerは、あなたのためにそれらを管理します。特定のスレッドを停止したい場合は、スレッドのソケットを閉じるだけで実行できます。しかし、すべての送信ロジックを所属するOnExecuteに移動すると、TIdTCPServerはエラーを処理してソケットを閉じることができます。

あなたOnConnectイベントハンドラは、IGクライアントが接続している場合にのみ、AThread->Dataを設定しているが、あなたのOnConnectOnDisconnectハンドラはTClientオブジェクトにアクセスしようとする前に、その状態をチェックされていません。

OnDisconnectIsClosingがtrueの場合、イベントハンドラがメモリをリークしています。そして、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 
} 
+0

は最近応えするために申し訳ありませんが、ありがとうございました。 –

+0

レミーありがとうございました。最近申し訳ありませんが返答しました。私にメモリリークの問題を指摘しました。私はそれを見るだろう。私は何か憂鬱を感じた。まず、あなたの提案からすべてのクライアントにデータを送信する定期的なイベントは見られませんでした。タイマーコールバック関数は、データを定期的に更新するだけです。サーバーが各クライアントに同じデータを送信する方法を確認するには?次に、UpdateServer1Data関数でCriticalSectionを使用しましたが、それは安全ですか?「(Data2Clientは前回のイベント以降に更新されました)」を正しく設定しましたか? (クライアントのコードを問題にします) –

+0

@BeezWeng各タイマーイベントがすべてのクライアントにデータを送信することが重要な場合は、すべてのタイマーイベントでクライアントをループすることを避けることはできません。しかし、タイマーイベントの中で実際の送信を直接行うことはお勧めしません。 –

関連する問題