В следующем коде у меня есть простой сервер, который отправляет сообщение всем клиентам 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();
}
Сообщение об ошибке означает, что вы обращаетесь к чему-то, что смещено на -48 байт от нулевого указателя. Я вижу все виды проблем с этим кодом, наименьшее из которых — то, что вы обращаетесь к вещам небезопасным способом, поэтому у вас есть условия гонки, которые могут повредить память, среди других возможных проблем. Например, ваш OnExecute
обработчик событий не защищает psRx
объект из параллельного доступа, поэтому несколько клиентов могут заполнить его данными в одно и то же время, повредив его содержимое.
TIdTCPServer
это многопоточный компонент, его события запускаются в контексте рабочих потоков, а не основного потока пользовательского интерфейса, поэтому ваши обработчики событий ДОЛЖНЫ использовать поточно-ориентированное кодирование.
Кроме того, то, что вы делаете, не является самым безопасным способом обработки асинхронной связи с 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;
}
}
Других решений пока нет …