2017-07-03 4 views
-4

次のコードでは、すべてのクライアントに1秒間に2回メッセージを送信し、別のメッセージを1分間に8〜10回送信する単純なサーバーを使用しています。単純なIndyサーバーコードを使用したアクセス違反

アクセス違反で00479740読み出しアドレス

しかし、唯一の少数のシステムでFFFFFFD0、そして唯一の1または2回日:

問題は、私は、実行時にエラーを取得しています。このソフトウェアは1日に約10時間働きます。

私はICSライブラリで同様のコードを使用しようとしましたが、うまくいくようです。

このコードで何が問題になっていますか?それをコード化する良い方法はありますか?

void __fastcall TDataNet::DataModuleCreate(TObject *Sender) 
{ 
    listaClient= new TThreadList(); 
    psTx= new TStringList(); 
    psRx= new TStringList(); 
} 

void __fastcall TDataNet::DataModuleDestroy(TObject *Sender) 
{ 
    IdTCPServer1->Active= false; 
    listaClient->Free(); 
    delete psTx; 
    delete psRx; 
} 

void __fastcall TDataNet::Send(TStrings *ps, TIdContext *AContext) 
{ 
    TList *lista; 
    static int cntSend= 0; 

    try 
    { 
     lista= listaClient->LockList(); 
     if(AContext != NULL) 
     { 
      AContext->Connection->IOHandler->Write(ps, true, TIdTextEncoding_UTF8); 
     } 
     else 
     { 
      for(int i=0; i < lista->Count; i++) 
       ((TDatiClient*)lista->Items[i])->pThread->Connection->IOHandler->Write(ps, true, TIdTextEncoding_UTF8); 
     } 
    } 
    __finally 
    { 
     listaClient->UnlockList(); 
    } 
} 

void __fastcall TDataNet::SetCambioPilota(void) 
{ 
    unsigned short hh, mm, ss, ms, hh1, mm1, ss1, ms1; 
    unsigned short hh2, mm2, ss2, ms2, hh3, mm3, ss3, ms3; 
    unsigned short hh4, mm4, ss4, ms4, dd4; 
    unsigned short hh5, mm5, ss5, ms5, dd5; 
    TStrings *ps; 
    UnicodeString s; 

    try 
    { 
     ps= psTx; 
     ps->Clear(); 

     s= "<CAMBIO_PILOTA>"; 
     ps->Add(s); 
     for(int i=0; i < MAX_PILOTI; i++) 
     { 
      s.sprintf(L"<Pilota%02x= I%x,\"A%s\",\"C%s\",\"F%s\",f%x>", 
       i+1, gara.pilota[i].idnome, 
       gara.pilota[i].nome.c_str(), gara.pilota[i].nick.c_str(), 
       gara.pilota[i].nomeTeam.c_str(), gara.pilota[i].idPilotaT); 
      ps->Add(s); 
     } 
     s= "<END_CAMBIO_PILOTA>"; 
     ps->Add(s); 

     Send(ps); 
    } 
    catch(...){} 
} 

void __fastcall TDataNet::SetDatiGara(void) 
{ 
    TStrings *ps; 
    UnicodeString s; 

    try 
    { 
     ps= psTx; 
     ps->Clear(); 

     s= "<DATI_GARA>"; 
     ps->Add(s); 

     s.sprintf(L"<eve=%d,A%x,B%x,C%x,D%x,E%x,F%x,G%x,H%x,I%x,J%x,K%x>", DataB->GetEventoInCorso().idEvento, 
       DataB->GetEventoInCorso().numEvento, DataB->GetEventoInCorso().subEvento, 
       DataB->GetNextEvento().idEvento, DataB->GetNextEvento().numEvento, DataB->GetNextEvento().subEvento, 
       gara.tkTempo, gara.tkDurata - gara.tkTempo, 
       gara.laps, gara.gDurata > 0 ? (gara.gDurata - gara.laps):0, gara.flInCorso ? (gara.gDurata > 0 ? 2:1):0, 
       gara.flFineGara); 
     ps->Add(s); 

     s= "<END_DATI_GARA>"; 
     ps->Add(s); 

     Send(ps); 
    } 
    catch(...){} 
} 

void __fastcall TDataNet::Timer1Timer(TObject *Sender) 
{ 
    Timer1->Enabled= false; 
    SetDatiGara(); 
    Timer1->Enabled= true; 
} 

void __fastcall TDataNet::IdTCPServer1Connect(TIdContext *AContext) 
{ 
    TDatiClient* dati; 

    dati= new TDatiClient; 
    dati->pThread= AContext; 
    AContext->Connection->IOHandler->ReadTimeout= 200; 
    AContext->Data= (TObject*)dati; 

    try 
    { 
     TList* lista; 
     lista= listaClient->LockList(); 
     lista->Add(dati); 
     connessioni= lista->Count; 
     if(FmainWnd) 
      PostMessage(FmainWnd, WM_EVENTO_TCP, ID_CONNESSO, lista->Count); 

     int idEvento= DataB->GetEventoInCorso().idEvento; 
     if(idEvento) 
      SetCambioStato(idEvento, STATO_EVENTO_START, AContext); 
    } 
    __finally 
    { 
     listaClient->UnlockList(); 
    } 
} 

void __fastcall TDataNet::IdTCPServer1Disconnect(TIdContext *AContext) 
{ 
    TDatiClient* dati; 

    dati= (TDatiClient*)AContext->Data; 
    AContext->Data= NULL; 

    try 
    { 
     listaClient->Remove(dati); 

     TList* lista; 
     lista= listaClient->LockList(); 
     connessioni= lista->Count; 

     if(FmainWnd) 
      PostMessage(FmainWnd, WM_EVENTO_TCP, ID_DISCONNESSO, lista->Count); 
    } 
    __finally 
    { 
     listaClient->UnlockList(); 
    } 
    delete dati; 
} 

void __fastcall TDataNet::IdTCPServer1Execute(TIdContext *AContext) 
{ 
    Sleep(100); 
    try 
    { 
     AContext->Connection->IOHandler->ReadStrings(psRx, -1); 

     if(psRx->Count >= 2 && psRx->Strings[0] == "<LAST_MINUTE>" && psRx->Strings[psRx->Count-1] == "<END_LAST_MINUTE>") 
     { 
      psRx->Delete(0); 
      psRx->Delete(psRx->Count-1); 
      if(FmainWnd) 
       SendMessage(FmainWnd, WM_EVENTO_TCP, ID_LAST_MINUTE, (unsigned int)psRx); 
     } 

     psRx->Clear(); 
    } 
    catch(...) {} 

    AContext->Connection->CheckForGracefulDisconnect(); 
} 
+1

デバッグを実行します。どのコード行で例外が発生しているかを調べる。スタックオーバーフローはデバッガではありません。 –

+0

エラーメッセージは、NULLポインターから-48バイトオフセットしたものにアクセスしていることを意味します。私はこのコードであらゆる種類の問題を見る。これは、TIdTCPServerとのサーバーツークライアント通信の実装を推奨するものではありません。より安全であるためには書き直す必要があります。私は明日の答えを投稿します。 –

+0

レミーありがとう、あなたのコードを調べてください – Mirco

答えて

0

エラーメッセージは、NULLポインタから-48バイトオフセットしたものにアクセスしていることを意味します。このコードではあらゆる種類の問題がありますが、スレッドセーフな方法で物事にアクセスしていることが最も少なく、メモリを破壊する可能性のある競合状態があります。たとえば、OnExecuteイベントハンドラはpsRxオブジェクトを同時アクセスから保護していないため、複数のクライアントがまったく同じ時刻にデータを入力してコンテンツを破損する可能性があります。

TIdTCPServerは、マルチスレッドコンポーネントです。そのイベントは、メインUIスレッドではなく、ワーカースレッドのコンテキストで発生するため、イベントハンドラはスレッドセーフなコーディングを使用する必要があります。

さらに、とにかくTIdTCPServerとの非同期通信を処理する最も安全な方法ではありません。代わりに次のようなものをお勧めします。

class TDatiClient : public TIdServerContext 
{ 
public: 
    TIdThreadSafeObjectList *Queue; 
    bool QueueHasObjects; 

    __fastcall TDatiClient(TIdTCPConnection *AConnection, TIdYarn *AYarn, TThreadList* AList = NULL) 
     : TIdServerContext(AConnection, AYarn, AList) 
    { 
     Queue = new TIdThreadSafeObjectList; 
    } 

    __fastcall ~TDatiClient() 
    { 
     delete Queue; 
    } 

    void __fastcall Send(TStrings *ps) 
    { 
     TStringList *toSend = new TStringList; 
     try 
     { 
      toSend->Assign(ps); 

      TList *lista = Queue->LockList(); 
      try 
      { 
       lista->Add(toSend); 
       QueueHasObjects = true; 
      } 
      __finally 
      { 
       Queue->UnlockList(); 
      } 
     } 
     catch (const Exception &) 
     { 
      delete toSend; 
     } 
    } 
}; 

void __fastcall TDataNet::TDataNet(TComponent *Owner) 
    : TDataModule(Owner) 
{ 
    // this must be set before you activate the server... 
    IdTCPServer1->ContextClass = __classid(TDatiClient); 

    // do this at runtime instead of design-time so 
    // ContextClass can be set first... 
    IdTCPServer1->Active = true; 
} 

void __fastcall TDataNet::~TDataNet() 
{ 
    IdTCPServer1->Active = false; 
} 

void __fastcall TDataNet::Send(TStrings *ps, TIdContext *AContext) 
{ 
    static int cntSend = 0; 

    TList *lista = IdTCPServer1->Contexts->LockList(); 
    try 
    { 
     if (AContext) 
     { 
      // make sure the client is still in the list... 
      if (lista->IndexOf(AContext) != -1) 
       static_cast<TDatiClient*>(AContext)->Send(ps); 
     } 
     else 
     { 
      for (int i = 0; i < lista->Count; ++i) 
       static_cast<TDatiClient*>(static_cast<TIdContext*>(lista->Items[i]))->Send(ps); 
     } 
    } 
    __finally 
    { 
     IdTCPServer1->Contexts->UnlockList(); 
    } 
} 

void __fastcall TDataNet::SetCambioPilota() 
{ 
    UnicodeString s; 

    try 
    { 
     TStringList *ps = new TStringList; 
     try 
     { 
      s = _D("<CAMBIO_PILOTA>"); 
      ps->Add(s); 

      for (int i = 0; i < MAX_PILOTI; ++i) 
      { 
       s.sprintf(_D("<Pilota%02x= I%x,\"A%s\",\"C%s\",\"F%s\",f%x>), 

        // TODO: if SetCambioPilota() is ever called in a worker thread, 
        // make sure these values are accessed in a thread-safe manner! 
        i+1, gara.pilota[i].idnome, 
        gara.pilota[i].nome.c_str(), gara.pilota[i].nick.c_str(), 
        gara.pilota[i].nomeTeam.c_str(), gara.pilota[i].idPilotaT); 

       ps->Add(s); 
      } 

      s = _D("<END_CAMBIO_PILOTA>"); 
      ps->Add(s); 

      Send(ps); 
     } 
     __finally 
     { 
      delete ps; 
     } 
    } 
    catch (const Exception &) 
    { 
    } 
} 

void __fastcall TDataNet::SetDatiGara() 
{ 
    UnicodeString s; 

    try 
    { 
     TStringList *ps = new TStringList; 
     try 
     { 
      s = _D("<DATI_GARA>"); 
      ps->Add(s); 

      s.sprintf(_D("<eve=%d,A%x,B%x,C%x,D%x,E%x,F%x,G%x,H%x,I%x,J%x,K%x>"), 

       // TODO: if SetDatiGara() is ever called in a worker thread, 
       // make sure these values are accessed in a thread-safe manner! 
       DataB->GetEventoInCorso().idEvento, 
       DataB->GetEventoInCorso().numEvento, DataB->GetEventoInCorso().subEvento, 
       DataB->GetNextEvento().idEvento, DataB->GetNextEvento().numEvento, DataB->GetNextEvento().subEvento, 
       gara.tkTempo, gara.tkDurata - gara.tkTempo, gara.laps, 
       (gara.gDurata > 0) ? (gara.gDurata - gara.laps) : 0, 
       gara.flInCorso ? ((gara.gDurata > 0) ? 2 : 1) : 0, 
       gara.flFineGara); 

      ps->Add(s); 

      s = _D("<END_DATI_GARA>"); 
      ps->Add(s); 

      Send(ps); 
     } 
     __finally 
     { 
      delete ps; 
     } 
    } 
    catch (const Exception &) 
    { 
    } 
} 

void __fastcall TDataNet::Timer1Timer(TObject *Sender) 
{ 
    Timer1->Enabled = false; 
    SetDatiGara(); 
    Timer1->Enabled = true; 
} 

void __fastcall TDataNet::IdTCPServer1Connect(TIdContext *AContext) 
{ 
    TDatiClient* dati = static_cast<TDatiClient*>(AContext); 

    AContext->Connection->IOHandler->DefStringEncoding = TIdTextEncoding_UTF8; 

    TList* lista = IdTCPServer1->Contexts->LockList(); 
    try 
    { 
     // TODO: this event is fired in a worker thread, so make sure 
     // that connessioni, DataB, and SetCambioStato() are all being 
     // accessed in a thread-safe manner! 

     int connessioni = lista->Count; 
     if (FmainWnd) 
      PostMessage(FmainWnd, WM_EVENTO_TCP, ID_CONNESSO, connessioni); 

     int idEvento = DataB->GetEventoInCorso().idEvento; 
     if (idEvento) 
      SetCambioStato(idEvento, STATO_EVENTO_START, AContext); 
    } 
    __finally 
    { 
     IdTCPServer1->Contexts->UnlockList(); 
    } 
} 

void __fastcall TDataNet::IdTCPServer1Disconnect(TIdContext *AContext) 
{ 
    TDatiClient* dati = static_cast<TDatiClient*>(AContext); 

    TList* lista = IdTCPServer1->Contexts->LockList(); 
    try 
    { 
     int connessioni = lista->Count - 1; 

     if (FmainWnd) 
      PostMessage(FmainWnd, WM_EVENTO_TCP, ID_DISCONNESSO, connessioni); 
    } 
    __finally 
    { 
     IdTCPServer1->Contexts->UnlockList(); 
    } 
} 

void __fastcall TDataNet::IdTCPServer1Execute(TIdContext *AContext) 
{ 
    TDatiClient* dati = static_cast<TDatiClient*>(AContext); 
    TStringList *ps; 

    if (dati->QueueHasObjects) 
    { 
     TObjectList *objs = new TObjectList(false); 
     try 
     { 
      TList *lista = dati->Queue->LockList(); 
      try 
      { 
       objs->Assign(lista); 
       lista->Clear(); 
       objs->OwnsObjects = true; 
      } 
      __finally 
      { 
       dati->QueueHasObjects = (lista->Count > 0); 
       dati->Queue->UnlockList(); 
      } 

      for (int i = 0; i < objs->Count; ++i) 
      { 
       ps = static_cast<TStringList*>(objs->Items[i]); 
       AContext->Connection->IOHandler->Write(ps, true); 
      } 
     } 
     __finally 
     { 
      delete objs; 
     } 
    } 

    if (AContext->Connection->IOHandler->InputBufferIsEmpty()) 
    { 
     AContext->Connection->IOHandler->CheckForDataOnSource(200); 
     if (AContext->Connection->IOHandler->InputBufferIsEmpty()) 
     { 
      AContext->Connection->IOHandler->CheckForDisconnect(); 
      return; 
     } 
    } 

    ps = new TStringList; 
    try 
    { 
     AContext->Connection->IOHandler->ReadStrings(ps, -1); 

     if ((ps->Count >= 2) && (ps->Strings[0] == _D("<LAST_MINUTE>")) && (ps->Strings[ps->Count-1] == _D("<END_LAST_MINUTE>"))) 
     { 
      ps->Delete(0); 
      ps->Delete(ps->Count-1); 

      if (FmainWnd) 
       SendMessage(FmainWnd, WM_EVENTO_TCP, ID_LAST_MINUTE, reinterpret_cast<LPARAM>(ps)); 
     } 
    } 
    __finally 
    { 
     delete ps; 
    } 
} 
+0

お返事ありがとうございました。 – Mirco

+0

何らかの理由でクライアントがフリーズしても、サーバが応答を待っている(私はACKと思っています)、サーバのロックを待っている間にメインスレッドがロックされたままになっていることに気付きました。これを避ける方法はありますか? 追加する前に文字列をコピーする理由があります TStringList * toSend = new TStringList; try { toSend-> Assign(ps); TList * lista = Queue-> LockList(); try { lista-> Add(toSend); ありがとう – Mirco

+0

@Mirco申し訳ありませんが、重要なコードを残しました.OnExecuteを使用してキューのローカルコピーを作成し、メインキューをロック解除してからキューに入れたアイテムを送信しました。私は今それを追加しました。 –

関連する問題