Absturz wegen CloseHandle(hThread)?



  • Hallo,

    ich habe folgendes Testszenario:
    Aus einem Konsolenprogramm starte ich einen ListenThread, der in einer Schleife via blockierendem accept auf eine eingehende Verbindung wartet.
    Wenn eine eintrudelt, startet er einen Kommunikationsthread, der eingehende Daten liest und im Moment einfach die Anzahl gelesender Bytes in die Konsole schreibt.
    Ein TestClient connected, schickt 1000Byte, disconnected sofort wieder und terminiert.
    Der Kommunikationsthread ist also sehr schnell fertig und beendet sich dann wieder.

    Im Listenthread ist die nächste Instruktion nach dem Start des Kommunikationsthreads ein CloseHandle(hThread), da ich das Handle nicht benötige.

    Wenn ich nun den Client in einer zweiten Konsole mehrmals nacheinander starte, stürzt mein Serverprogramm ab, manchmal schon beim zweiten Versuch, manchmal erst etwas später.

    Dies kann ich verhindern - so scheint es zumindest im Moment - wenn ich nach dem Start des Kommunikationsthreads mit Sleep(500) warte, bevor ich das Handle mit CloseHandle(hThread) schließe.

    Gibt es dafür eine Erklärung?



  • Zeig mal den relevanten Code.
    Simon



  • Server: (Wobei in Zeile 104 die Warteinstruktion steht)

    #include <winsock2.h> 
    #include <ws2tcpip.h>
    #include <windows.h>
    #include <process.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <conio.h>
    
    #define DEFAULT_BUFLEN 512
    #define DEFAULT_PORT "8717"
    
    static boolean ProgrammEnde;
    
    void MyCommThread (void * Param)
    {
    	 char recvbuf[DEFAULT_BUFLEN];
        int iResult, iSendResult;
        int recvbuflen = DEFAULT_BUFLEN;
    
        boolean fehler;
    
    	printf("Thread gestartet\n");
    
    	SOCKET ClientSocket, *pClientSocket;
    	int error;
    
    	ClientSocket = *(SOCKET *) Param;
    	pClientSocket = (SOCKET *) Param;
    
    	fehler = false; 
    
    	while (!ProgrammEnde && !fehler)
    	{
    
        // Receive until the peer shuts down the connection
                   printf("ClientSocket wartet auf Daten ...");
            iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
            if (iResult > 0) {
                printf("Bytes received: %d\n", iResult);
            }
            else if (iResult == 0) {
                printf("Connection closing...\n");
                fehler = true;
            }
            else  {
            		error = WSAGetLastError();
            		if (error != WSAEWOULDBLOCK)
                {
                	printf("recv failed: %d\n", WSAGetLastError());
                	fehler = true;
                }
            }
    	}
    	 closesocket(ClientSocket);         
    	 *pClientSocket = INVALID_SOCKET; 
    	 printf("Threadende\n");
    	 _endthread();
    
    }
    
    void MyListenThread (void * Param)
    {
    	SOCKET  ListenSocket, TmpSocket;
    	int error;
    	HANDLE hThread;
    
    	const int MaxClients = 3;
    	SOCKET ClientSocket[MaxClients];	
    
    	for (int i = 0; i < MaxClients; i++)
    		ClientSocket[i] = INVALID_SOCKET;
    
    	ListenSocket = *(SOCKET *) Param;
    
    	while (!ProgrammEnde)
    	{
    	    // Accept a client socket
    	    printf("warte auf Client ...\n");
    	    TmpSocket = accept(ListenSocket, NULL, NULL);
    	    printf("accepted\n");
    	    if (TmpSocket == INVALID_SOCKET) {
    	    	  error = WSAGetLastError();
    	    	  if (error != WSAEWOULDBLOCK)
    	    	  {
    	    	  		ProgrammEnde = true;
    	        		printf("accept failed: %d\n", error);
    	        		break;
    	        }	
    	    }
    	    else 
    	    {
    	    	int i;
    	    	for (i = 0; i < MaxClients; i++)
    	    	{
    	    		printf("\ni = %d\n", i);
    	    		if (ClientSocket[i] == INVALID_SOCKET)
    	    		{
    	    			ClientSocket[i] = TmpSocket;
    	            printf("Starte CommThread ... \n"); 	
    	 			  	hThread = (HANDLE) _beginthread(MyCommThread, 0, &ClientSocket[i]);
    			    	Sleep(500);
    			    	CloseHandle (hThread);
    					break;
    			   }
    			}
    
    			 if (i >= MaxClients)
    			 {
    			 	 printf("Zu viele Clients\n");
    			 	 closesocket(TmpSocket);
    			 }
    	    }
    	 }
    
    	 for (int i = 0; i < MaxClients; i++)
    	 	  if (ClientSocket[i] != INVALID_SOCKET)
    	 	  	  closesocket (ClientSocket[i]);
    
    	 closesocket(ListenSocket);
    	 _endthread();
    }
    
    int main(void) 
    {
    	 HANDLE hThread;
        WSADATA wsaData;
        SOCKET ListenSocket = INVALID_SOCKET,
               ClientSocket = INVALID_SOCKET;
        struct addrinfo *result = NULL,
                        hints;
        char recvbuf[DEFAULT_BUFLEN];
        int iResult, iSendResult;
        int recvbuflen = DEFAULT_BUFLEN;
    
        // Initialize Winsock
        iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
        if (iResult != 0) {
            printf("WSAStartup failed: %d\n", iResult);
            return 1;
        }
    
        ZeroMemory(&hints, sizeof(hints));
        hints.ai_family = AF_INET;
        hints.ai_socktype = SOCK_STREAM;
        hints.ai_protocol = IPPROTO_TCP;
        hints.ai_flags = AI_PASSIVE;
    
        // Resolve the server address and port
        iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
        if ( iResult != 0 ) {
            printf("getaddrinfo failed: %d\n", iResult);
            WSACleanup();
            return 1;
        }
    
        // Create a SOCKET for connecting to server
        ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
        if (ListenSocket == INVALID_SOCKET) {
            printf("socket failed: %ld\n", WSAGetLastError());
            freeaddrinfo(result);
            WSACleanup();
            return 1;
        }
    
        // Setup the TCP listening socket
        iResult = bind( ListenSocket, result->ai_addr, (int)result->ai_addrlen);
        if (iResult == SOCKET_ERROR) {
            printf("bind failed: %d\n", WSAGetLastError());
            freeaddrinfo(result);
            closesocket(ListenSocket);
            WSACleanup();
            return 1;
        }
    
        freeaddrinfo(result);
    
        iResult = listen(ListenSocket, SOMAXCONN);
        if (iResult == SOCKET_ERROR) {
            printf("listen failed: %d\n", WSAGetLastError());
            closesocket(ListenSocket);
            WSACleanup();
            return 1;
        }
    
        ProgrammEnde = false;
    
       hThread = (HANDLE) _beginthread(MyListenThread, 0, &ListenSocket);
    
       CloseHandle(hThread); 
    
       getch();
       closesocket(ListenSocket);
       ProgrammEnde = true;
       Sleep(1000);
       WSACleanup();
       return 0;   
    }
    

    Client:

    #include <winsock2.h> 
    #include <ws2tcpip.h>
    #include <windows.h>
    #include <process.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <conio.h>
    
    #define DEFAULT_BUFLEN 512
    #define DEFAULT_PORT "8717"
    
    static boolean ProgrammEnde;
    
    void schickDaten(SOCKET *s)
    {
      SOCKADDR_IN sa_in;
      int error;
      boolean fehler;
      char buf[100000];
    
      // Connect zum Server
      memset (&sa_in, 0, sizeof (sa_in));
      sa_in.sin_family = AF_INET;
      sa_in.sin_port = htons(8717);
      //sa_in.sin_addr.s_addr = inet_addr("10.224.33.149");
      sa_in.sin_addr.s_addr = inet_addr("127.0.0.1");
      error = connect (*s, (SOCKADDR*) &sa_in, sizeof (sa_in));
    
      if (error)
      {
      	printf("Fehler beim Connect ... %d\n", WSAGetLastError());
      	fehler = true;
      }	
    
      send (*s, buf, 1000, 0);
    }
    
    int main(void) 
    {
    	 HANDLE hThread;
        WSADATA wsaData;
        SOCKET Socket = INVALID_SOCKET,
               ClientSocket = INVALID_SOCKET;
        struct addrinfo *result = NULL,
                        hints;
        char recvbuf[DEFAULT_BUFLEN];
        int iResult, iSendResult;
        int recvbuflen = DEFAULT_BUFLEN;
    
        // Initialize Winsock
        iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
        if (iResult != 0) {
            printf("WSAStartup failed: %d\n", iResult);
            return 1;
        }
    
        ZeroMemory(&hints, sizeof(hints));
        hints.ai_family = AF_INET;
        hints.ai_socktype = SOCK_STREAM;
        hints.ai_protocol = IPPROTO_TCP;
        hints.ai_flags = AI_PASSIVE;
    
        // Resolve the server address and port
        iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
        if ( iResult != 0 ) {
            printf("getaddrinfo failed: %d\n", iResult);
            WSACleanup();
            return 1;
        }
    
        // Create a SOCKET for connecting to server
        Socket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
        if (Socket == INVALID_SOCKET) {
            printf("socket failed: %ld\n", WSAGetLastError());
            freeaddrinfo(result);
            WSACleanup();
            return 1;
        }
    
        freeaddrinfo(result);
    
       schickDaten(&Socket);
       shutdown(Socket, SD_SEND);
    
       return 0;              
    }
    

  • Mod

    IMHO solltest Du Dir mal Gedanken über die Lebensdauer von Variablen machen und insbesondere von Zeigern auf die Daten.

    MyListenThread hat Daten auf seinem Stack, die Du mit einem Zeiger an den Thread MyCommThread weitergibst.
    Was passiert wenn MyListenThread vor MyCommThread terminiert?
    Der Speicher auf den param zeigt wird ungültig und Du zerscheßt den Sepicher!
    👎



  • Tjaaaaaaaaa ... eigentlich SOLL der Listen-Thread nicht zum Ende kommen, bevor alle Threads fertig sind.
    Aber Du hast wohl recht, das habe ich nicht sichergestellt ...
    Danke für den Hinweis, allerdings ist das nicht die Ursache für mein eingangs geschildertes Problem - das tritt nämlich auch dann auf, wenn ich das Array global definiere.

    Das erste printf im Komm.Thread wird im Absturzfall schon nicht mehr ausgeführt, obwohl _beginthread ein imo gültiges Handle (<> 1) und CloseHandle(hThread) true zurückgegeben hat.

    Es sieht also so aus, als ob der Komm.Thread abstürzt, bevor er "Thread gestartet" ausgeben kann, aber er macht doch vorher gar nix 😕



  • Belli schrieb:

    , obwohl _beginthread ein imo gültiges Handle (<> 1) und CloseHandle(hThread) true zurückgegeben hat.

    _beginthread liefert im Fehlerfall nicht 1, sondern -1. Außerdem liefert _beginthread niemals ein gültiges Handle. Dieses wird nämlich bereits vor der Rückkehr wieder geschlossen. Von daher solltest Du immer auf _beginthreadex ausweichen, denn nur mit dem von dieser Funktion gelieferten Handle kannst Du auch etwas anfangen.

    Achtung: _beginthreadex liefert im Fehlerfall 0, nicht -1.

    BTW: Deine Aufrufe von _endthread machen mich schwindelig. Schmeiß das raus, das braucht kein Mensch.



  • Jo, hätte ich mal ein bißchen weitergelesen in der MSDN ...
    Bisher habe ich immer CreateThread(...) benutzt, aber das soll man ja vermeiden, wenn die Möglichkeit besteht, daß aus dem Thread C-Runtime - Routinen aufgerufen werden <-- und da bin ich mir häufig nicht sicher.
    Deshalb bin ich auf _beginthread umgestiegen, aber das hätte ich mir wohl genauer durchlesen sollen.

    Herzlichen Dank an alle!!!


Anmelden zum Antworten