使用简单的Indy服务器代码访问冲突

时间:2017-07-03 06:04:34

标签: c++ c++builder indy indy10

在下面的代码中,我有一个简单的服务器,每秒向所有客户端发送2次消息,每分钟发送8-10次消息。

问题是我在运行时遇到错误:

  

访问冲突00479740读取地址FFFFFFD0

但仅限于少数系统,每天只有1或2次。该软件每天工作约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 :(得分: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;
    }
}