Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

1

08.05.2010, 16:40

kleines Problem mit CriticalSections

Hallo,
wie bereits im Titel geschrieben habe ich ein Problem mit CriticalSections. Ich habe eine Klasse, welche zwei davon benutzt, am Anfang in der Init Methode werden sie per InitializeCriticalSection() erzeugt, am Ende in der Shutdown Methode mit DeleteCriticalSection() wieder gelöscht. Das Problem ist, wenn ich das Programm beende, sagt mir die Laufzeitüberprüfung, dass "Run-Time Check Failure #2 - Stack around the variable 'Server' was corrupted." Ich bin mir sicher, dass es an den Sections liegt, weil ohne sie der Fehler nicht kommt. In der MSDN stand, das solche Fehler auftreten, wenn man versucht nach dem deleten auf die Sections zuzugreifen, was ich aber nicht mache. Habt ihr noch ne Idee wie ich den Fehler loswerde?

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

2

08.05.2010, 17:02

Ich glaube es hat nichts mit den Sections zu tun, sondern handelt sich um einen Fehler in deinem Programm.
Ist das zu lang, um es hier zu posten?

3

08.05.2010, 17:06

das geht wohl :)

es geht um eine Server Klasse für ein Netzwerk Spiel:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class NETWORKLIB_EXPORT CServer
{
    private:

        WSADATA wsa;
        SOCKADDR_IN addr;

        SOCKET ContactSocket;
        HANDLE ContactThreadHandle;
        DWORD ContactThreadId;

        HANDLE SendThreadHandle;
        DWORD SendThreadId;

        SOCKET* ClientSockets;
        HANDLE* ClientThreadHandles;
        DWORD* ClientThreadIds;

        std::queue<char*>* InputBuffer;
        std::queue<char*>* OutputBuffer;

        int MaxClients;

        static DWORD WINAPI ContactThread(LPVOID pParameters);
        static DWORD WINAPI ClientThread(LPVOID pParameters);
        static DWORD WINAPI SendThread(LPVOID pParameters);

        CRITICAL_SECTION csInputBuffer;
        CRITICAL_SECTION csOutputBuffer;

    public:

        void Create(int pPort, int pMaxClients);
        bool GetBuffer(int pClientId, char* pBuffer, int pBufferSize);
        void AddToBuffer(int pClientId, char* pBuffer);
        void Shutdown();
};


interessand sind eigentlich nur die methoden:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
void CServer::Create(int pPort, int pMaxClients)
{
    //maximale Anzahl Klienten speichern
    MaxClients = pMaxClients;

    //WSAStartup
    WSAStartup(MAKEWORD(2,0),&wsa);

    //Kontakt Socket erstellen
    ContactSocket=socket(AF_INET,SOCK_STREAM,0);

    //SOCKADDR_IN Struktur füllen
    ZeroMemory(&addr, sizeof(SOCKADDR_IN));
    addr.sin_family=AF_INET;
    addr.sin_port=htons(pPort);
    addr.sin_addr.s_addr=INADDR_ANY;

    //Kontakt Socket binden und in listen Modus setzen
    bind(ContactSocket, (SOCKADDR*)&addr, sizeof(SOCKADDR_IN));
    listen(ContactSocket, MaxClients);

    //ClientArrays erzeugen
    ClientSockets = new SOCKET[MaxClients];
    ClientThreadHandles = new HANDLE[MaxClients];
    ClientThreadIds = new DWORD[MaxClients];

    //Buffer erzeugen
    InputBuffer = new std::queue<char*>[MaxClients];
    OutputBuffer = new std::queue<char*>[MaxClients];

    //Clientarrays leer initialisieren
    for(int i=0;i<MaxClients;i++)
    {
        ClientSockets[i]        = 0;
        ClientThreadHandles[i]  = 0;
        ClientThreadIds[i]      = 0;
    }  

    //CriticalSections erzeugen
    InitializeCriticalSection(&csInputBuffer);
    InitializeCriticalSection(&csOutputBuffer);

    //Kontakt Thread starten
    ContactThreadHandle = CreateThread(NULL, 0, ContactThread, this, 0, &ContactThreadId);

    //Send Thread starten
    SendThreadHandle = CreateThread(NULL, 0, SendThread, this, 0, &SendThreadId);

}

void CServer::Shutdown()
{
    //Kontakt Thread und Socket schließen
    TerminateThread(ContactThreadHandle, 0);
    closesocket(ContactSocket);

    //Send Thread beenden
    TerminateThread(SendThreadHandle, 0);

    //Client Threads, Sockets schließen und Speicher freigeben
    for(int i=0;i<MaxClients;i++)
    {
        TerminateThread(ClientThreadHandles[i], 0);
        closesocket(ClientSockets[i]);

        while(!InputBuffer[i].empty())
        {
            delete InputBuffer[i].front();
            InputBuffer[i].pop();
        }
        while(!OutputBuffer[i].empty())
        {
            delete OutputBuffer[i].front();
            OutputBuffer[i].pop();
        }
    }

    //CriticalSections löschen
    //DeleteCriticalSection(&csInputBuffer);
    //DeleteCriticalSection(&csOutputBuffer);

    //Speicher freigeben
    delete [] ClientThreadIds;
    delete [] ClientThreadHandles;
    delete [] ClientSockets;
    delete [] InputBuffer;
    delete [] OutputBuffer;

    WSACleanup();
}

}


das eigentliche Programm macht noch nicht so viel, ist nur zum testen:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include "NetworkLib/global.h"

int main()
{
    CServer Server;
    CClient Client;
    Server.Create(1612, 10);
    Client.Init("127.0.0.1", 1612);
    Client.SetBuffer("test", 5);
    Client.SetBuffer("test1", 6);
    Client.SetBuffer("test11", 7);
    Client.SetBuffer("test111", 8);
    Client.SetBuffer("test1111", 9);*/
    Sleep(100);
    
    char tmp[512];
    

    while(true)
    {
        while(Server.GetBuffer(0, tmp, 512))
        {
            Server.AddToBuffer(0, tmp);
            std::cout<<"Server received: "<<tmp<<"Sent back!\r\n";
        }
    }

    Server.Shutdown();

    return 0;
}


hilft das schon oder soll ich noch mehr zeigen?

Helmut

5x Contest-Sieger

Beiträge: 692

Wohnort: Bielefeld

  • Private Nachricht senden

4

08.05.2010, 18:09

Hi,
TerminateThread sollte man nicht benutzen. Was ist, wenn dein Thread die CriticalSection gerade gelockt hat und dann gekillt wird? Dann löscht deine Klasse eine gelockte CriticalSection, und das Verhalten ist dann undefiniert. Und deine Datenstrukturen vielleicht auch Schrott. Allerdings ist das sehr wahrscheinlich nicht die Ursache deines Problems, du solltest es aber trotzdem lösen ;) Am Besten, indem du ein Event/Nachricht an die Threads schickst (oder ne volatile bool Variable verwendest), sich gefälligst selbst zu beenden. Oder noch besser, benutz erst garkeine Threads. Sockets lassen sich prima asynchron benutzen.

Dein eigentliches Problem wird vermutlich durch einen Bufferunderflow verursacht. Zeig mal den Header von CClient. Oder AddToBuffer schreibt auf den Buffer, mach den Parameter mal besser const.

Ciao
Helmut
Sei stets geduldig gegenüber Leuten, die nicht mit dir übereinstimmen. Sie haben ein Recht auf ihren Standpunkt - trotz ihrer lächerlichen Meinung. (F. Hollaender)

5

08.05.2010, 19:29

CClient.h

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class NETWORKLIB_EXPORT CClient
{
    private:

        WSADATA wsa;
        SOCKET s;
        SOCKADDR_IN addr;

        std::queue<char*> InputBuffer;
        std::queue<char*> OutputBuffer;

        HANDLE RecieveThreadHandle;
        DWORD RecieveThreadId;
        HANDLE SendThreadHandle;
        DWORD SendThreadId;

        static DWORD WINAPI RecieveThread(LPVOID pParameter);
        static DWORD WINAPI SendThread(LPVOID pParameters);

    public:

        bool Init(char* pIp, int pPort);
        bool GetBuffer(char* pBuffer, int pBufferSize);
        void SetBuffer(char* pBuffer, int pBufferSize);
        void Shutdown();
};


ich glaube nicht das AddToBuffer was falsch macht, ist sehr einfach:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
void CServer::AddToBuffer(int pClientId, char* pBuffer)
{
    char* tmp = new char[512];
    strcpy_s(tmp, 512, pBuffer);

    EnterCriticalSection(&csOutputBuffer);
    OutputBuffer[pClientId].push(tmp);
    LeaveCriticalSection(&csOutputBuffer);
}

Helmut

5x Contest-Sieger

Beiträge: 692

Wohnort: Bielefeld

  • Private Nachricht senden

6

08.05.2010, 21:14

Dann zeig mal noch Server::GetBuffer und Client::SetBuffer. (Übrigens etwas komische Namen für Methoden, die etwas Senden und Empfangen sollen:))

Ciao
Sei stets geduldig gegenüber Leuten, die nicht mit dir übereinstimmen. Sie haben ein Recht auf ihren Standpunkt - trotz ihrer lächerlichen Meinung. (F. Hollaender)

7

08.05.2010, 22:12

naja, die senden und empfangen ja auch garnicht, das machen ja die threads, die schreiben bzw. lesen nur die Buffer aus ;)

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
bool CServer::GetBuffer(int pClientId, char* pBuffer, int pBufferSize)
{
    EnterCriticalSection(&csInputBuffer);
    if(InputBuffer[pClientId].empty())
    {
        LeaveCriticalSection(&csInputBuffer);
        return false;
    }

    strcpy_s(pBuffer, pBufferSize, InputBuffer[pClientId].front());
    delete InputBuffer[pClientId].front();
    InputBuffer[pClientId].pop();

    LeaveCriticalSection(&csInputBuffer);
    return true;
}

bool CClient::GetBuffer(char* pBuffer, int pBufferSize)
{
    if(InputBuffer.empty()) return false;

    strcpy_s(pBuffer, pBufferSize, InputBuffer.front());
    delete InputBuffer.front();
    InputBuffer.pop();

    return true;
}

Helmut

5x Contest-Sieger

Beiträge: 692

Wohnort: Bielefeld

  • Private Nachricht senden

8

08.05.2010, 22:35

Wie Funktionen intern funktionieren ist ja grundsätzlich egal ;)

Aber ich fürchte ich seh da nichts, bis auf die grundlegende Problematik der sinnlosen Threads. Oder hast du in den gezeigten Codeausschnitten etwas entfernt, das du für unwichtig hielst?

Ich würde erstmal die ganzen Threads wegmachen und auf select umsteigen.

Ciao
Sei stets geduldig gegenüber Leuten, die nicht mit dir übereinstimmen. Sie haben ein Recht auf ihren Standpunkt - trotz ihrer lächerlichen Meinung. (F. Hollaender)

9

09.05.2010, 13:52

ich habe jetzt die threads entfernt und auf select() umgestellt. Läuft alles prima jetzt. Naja viel Arbeit für wenig, aber -> learning by doing würde ich mal sagen ne :)
vielen dank für eure hilfe

Werbeanzeige