CreateThread - keine Memberfunktion erlaubt ?



  • Kann meinem Vorredner nur zustimmen. select ist eindeutig die bessere Variante. Gibt es einen Grund, warum du das nicht nutzen willst 😕



  • @ Belli Achso ! Ja, so macht das alles Sinn ;P Gut, ich denke das wäre mal dann die beste Lösung für mich, so werde ich es wohl machen 😉 Muss ich mir nurnoch überlegen, wie ich das alles wieder an meine Gui-Klasse weiter kriege. Sollte aber ein leichtes sein. Generell habe ich wenn ich Klassen benutzer aber dauernd das Problem, dass ich mir überlegen muss, wie Klasse A Daten an Klasse B weiter gibt, die diese braucht um zu funktionieren. Nervig 😕

    @ TyRoXx Nun ja, wie gesagt : Auch mit select() komme ich um Multithreading nicht rum. Zmnd wüsste ich nicht wie.



  • Wo ist das Problem mit select?

    Einfach
    PeekMessage + select und schon kannste den ganzen Thread Kram vergessen. Ist einfacher und eleganter. Was will man mehr? 😃



  • Hmmmm schrieb:

    @ TyRoXx Nun ja, wie gesagt : Auch mit select() komme ich um Multithreading nicht rum. Zmnd wüsste ich nicht wie.

    Tutorial



  • Achsoo, mit PeekMessage ist das natürlich kein Problem ;P Dann wird meine Klasse da ja ziemlich überflüssig. Werde das mal eben umsetzen.
    Auf jeden Fall : Danke an alle 😉 Das tut werd ich mir auch nochmal reinziehen, obwohl mir die Funktionsweise von select eigentlich klar ist. Naja, mehr oder weniger ;P
    Ich werd mich hier melden falls noch Probleme auftauchen.

    Schönen Abend wünsche ich noch !



  • Sooo, habe mal wieder ein Problem ;P
    Edit : Lustig, zum 3. mal hab ich hier beim Posten und kommentieren des Codes den fehler gefunden ;P Aber es bleibt ein Fehler :
    Ich habe bei jedem Clienten eine List-Box, die die Namen aller bisher Clients halten Soll. D.h wenn einer neuer Client auf den Server connected, muss dessen Namen bei jedem anderen Client zur Listbox hinzugefügt werden. Das ist im Prinzip so wie bei ICQ/MSN/Whatever die Kontaktliste.

    Hier erstmal der Server :

    #define MAX_CLIENTS 10
    
    // Ganzen Verbindungs-relevanten Sachen wie Socket erzeugen, SOCKADDR_IN füllen, binden, listn etc
    
    struct client_struct {
      SOCKET client;
      char name[254];
    };
    FD_SET fdSet;
    long rc = 0;
    char buf[1024];
    client_struct clients[MAX_CLIENTS];
    
    // Hier dann die eigentliche Select Schleife
    while(1) {
        // das Set leeren
        FD_ZERO(&fdSet);
        // "acceptSocket" ist das Socket zum Verbindungen annehmen
        FD_SET(acceptSocket,&fdSet); 
    
        // Alle bisher verbunden Clients erstmal in das set packen
        for(i=0;i<MAX_CLIENTS;i++) {
    
          if(clients[i].client!=INVALID_SOCKET) {
    
            FD_SET(clients[i].client,&fdSet);
          }
        }
    
        rc=select(0,&fdSet,NULL,NULL,NULL); 
        if(rc==SOCKET_ERROR) {
    
          cout << "Error: select failed, error code: " << WSAGetLastError() << endl;
    	  closesocket(acceptSocket);
    	  WSACleanup();
          return 1;
        }
    
        // Falls das Verbindunhs-Socket immernoch drin ist -> Verbindung annehmen
        if(FD_ISSET(acceptSocket,&fdSet)) {
          // Leeren Platz suchen
          for(i=0;i<MAX_CLIENTS;i++) {
    
            if(clients[i].client==INVALID_SOCKET) {
    
              clients[i].client=accept(acceptSocket,NULL,NULL);
              cout << "accepted a new client (" << i << ")" << endl;
              break;
            }
          }
        }
    
        // So, jetzt noch überprüfen ob irgendein anderes Socket was senden will 
        for(i=0;i<MAX_CLIENTS;i++) {
    
          if(clients[i].client==INVALID_SOCKET) {
            // cout << "Cannot use this client ... " << " i is : " << i << endl;
            continue; 
          }
    
          if(FD_ISSET(clients[i].client,&fdSet)) {
             // cout << "But this client works ! " << "i is : " << i << endl;
            rc = recv(clients[i].client,buf, sizeof(buf) ,0);
    
            // Falls recv 0 return oder eben SOCKET_ERROR dann hat der entsprechende Client die Verbindung gelöst
            if(rc==0 || rc==SOCKET_ERROR) {
    
              cout << clients[i].name << " pissed off, what a wanker ... " << endl;
    		  // Das möchte ich an dieser Stelle auch an alle anderen Clients weitergeben 
                      // Hier fehlt eigentlich noch das Un-Adden des Client von der Listbox, aber das mache ich später
    		  char temp[300];
    		  strncpy(temp, clients[i].name, sizeof(temp));
    		  strncat(temp, " pissed off, what a wanker ...\n", ( sizeof(temp) ) - strlen(temp) );
    		  for(int j = 0; j < MAX_CLIENTS; j++) {
    		  if(j != i && ( clients[j].client != INVALID_SOCKET ) )
    			  send(clients[j].client, temp , strlen(temp), 0 );
    		  }
              closesocket(clients[i].client); // Socket schlißen     
              clients[i].client=INVALID_SOCKET; // Platz im struct wieder frei geben
              memset( clients[i].name, 0, sizeof(clients[i].name) ); // Und den Namens-buffer auch leeren
            }
    
            else {
    
              buf[rc]='\0';
              cout << buf << endl; // Diese Ausgaben exisitieren beim Server immer zur Überprüfung
    
    		  // Ich habe hier art "special messages" erfunden, mit der Sachen wie Namen etc ausgetauscht werden
                      // Jeder Client schickt nach dem Connecten direkt diese special Message den Server, gefolgt von seinem Namen
                      // Daher der 2. recv call, ohne Überprüfung
    
    		  if(strcmp(buf, \r\r##SENDING CLIENT NAME##\r\r" ) == 0) {
    		     // cout << "Special message got send ! " << endl;
    		     rc = recv( clients[i].client, clients[i].name, sizeof( clients[i].name ) , 0); // Speicherung des gesendeten Namens im struct
    		     // cout << "Realised that there is a new client connected ... " << endl;
    		     clients[i].name[rc] = '\0';
    		     cout << clients[i].name << endl;
                         // Dashier ist eine 2. special-message, um das oben beschriebene zu realiseren ( Listbox mit allen client-names ) 
    
                         for ( int j = 0; j < MAX_CLIENTS ; j++ ) {
    		         if ( clients[j].client != INVALID_SOCKET && j != i ) {
    			    send ( clients[j].client , "\r\r##NEW CLIENT CONNECTED##\r\r", 28, 0 );
    			    Sleep(100); // Ist scheinbar nötig, sonst kommen die Strings als ein einziger an
    			    send ( clients[j].client, clients[i].name , strlen(clients[i].name) , 0 );
    
    			 }
    
    		     }
    		  }
                      // Falls das normaler Text ist soll der einfach erstmal an alle ebenfalls connectete Clients weitergegeben werden. Macht im Moment
                      // wenig Sinn, ist aber auch nicht das Problem.
    		  else {
    			  cout << clients[i].name << " sends : "  << buf << endl;
                              // Hier wird die Nachricht wieder weiter gegeben
    			  char temp[1224];
    			  strncpy(temp, clients[i].name, sizeof(temp) );
    			  strncat(temp, " sends : ", ( sizeof(temp) ) - strlen(temp) );
    			  strncat(temp, buf, ( sizeof(temp)) - strlen(temp)  );
    		          for(int j = 0; j < MAX_CLIENTS; j++) {
    		              if(j != i && ( clients[j].client != INVALID_SOCKET ) )
    			         send(clients[j].client, temp , strlen(temp) , 0 );
    		          }
    
    		  }
    		}
    	}
    
         }
      }
    

    Dieses "Prinzip" funktionierte bei den Console-Clients auch einwandfrei. Die Nachrichten wurden richtig weitergegeben, die Namen richtig gespeichert etc.
    Das Problem : Die Listbox des GUI-Clients hängt sich auf und kann nurnoch abgewürgt werden.

    Eben noc der Code, den der GUI-Client für die Connection ausführt :

    BOOL Connecter::Connect( char* ipAdress, int port, char* clientName ) {
         if( WSAStartup(MAKEWORD(2,0),&wsa) != 0 )
            return FALSE;
         s = socket (AF_INET, SOCK_STREAM , 0 );
         if(s == INVALID_SOCKET )
            return FALSE;
         addr.sin_family = AF_INET;
         addr.sin_port=htons(port);
         addr.sin_addr.s_addr=inet_addr(ipAdress);
         if( connect(s,(SOCKADDR*)&addr,sizeof(SOCKADDR)) == SOCKET_ERROR ) {
            MessageBox(NULL,"Connection failed ...", "Error", MB_OK );
            CleanUp();
            return FALSE;
         }
         // MessageBox ( NULL, "Connected sucessfully" , "Success", MB_OK);
         send(s,"\r\r##SENDING CLIENT NAME##\r\r", 28, 0 );
         Sleep(100);
         send(s, clientName, strlen(clientName), 0);
    
         return TRUE;         
    }
    

    Die Parameter wurden richtig übergeben, da ja die Verbindung etc. klappt. So, in der Message-Schleife des GUI-Clients habe ich es so gemacht, wie ihr vorgeschlagen habt : PeekMessage() mit select() :

    while (1)
        {
            if( PeekMessage (&messages, NULL, 0, 0, PM_REMOVE ) ) {
            /* Translate virtual-key messages into character messages */
            TranslateMessage(&messages);
            /* Send message to WindowProcedure */
            DispatchMessage(&messages);
            }
    
            else {
               if( !( gui.getConnecterClass().getSocket() == INVALID_SOCKET) ) {
                  FD_SET(gui.getConnecterClass().getSocket(), &set );
                  int rc = select(0,&set,NULL,NULL,NULL);
                  if( rc == SOCKET_ERROR) {
                     MessageBox( NULL , "Select failed ... Closing the programm", "Error" , MB_OK );
                     return 1;
                  }
                  if ( FD_ISSET( gui.getConnecterClass().getSocket() , &set ) ) {
                      char buf[1024];
                      // Normalerweise sendet der Server so : Name des senden Clients gefolgt von dem Text.
                      // Das ist aber nicht der Fall, wenn er eine special-message schickt, also müssen wir das erst überprüfen
                      rc = recv ( gui.getConnecterClass().getSocket() , buf , sizeof(buf) , 0 );
                      if ( rc == 0 || rc == SOCKET_ERROR ) {
                          gui.TerminateClients();
                          return 0;
                      }
    
                      buf[rc] = 0;
                      // Eben hier 
                      if( strcmp( buf , "\r\r##NEW CLIENT CONNECTED##\r\r" ) == 0 ) {
                         memset( buf , 0 , sizeof(buf ) );
                         rc = recv ( gui.getConnecterClass().getSocket() , buf , sizeof(buf) , 0 );
                         buf[rc] = 0;
                         //MessageBox(NULL, buf , "Got smth", MB_OK);
                         // Wie die special-message schon sagt, muss nun ein neuer Client hinzugefügt werden. addClient erwartet als Parameter den Namen des 
                         // hinzuzufügenden Clients
                         gui.addClient( buf );
                      }
                      else {
                         // Wie gesagt, wenn es KEINE special war, dann hat er zuerst den Namen geschickt und dann den Text 
                         // Also brauchen wir jetzt einen 2. Buffer um den Text zu speichern
                         char buf2[1024];
                         rc = recv ( gui.getConnecterClass().getSocket() , buf2, sizeof(buf2) , 0 );
                         // UpdateWindowOutput braucht als 1. Parameter den Text und als 2. den Chat-Partner, um das richtige Fenster zu finden
                         gui.UpdateWindowOutput( buf2 , buf );
                      }
                  }   
               }
            }
        }
    

    Oh man, je länger das hier wird desto unwahrscheinlich wird es, dass jemand sich die Mühe macht das hier alles durchzu lesen 😕
    Naja ... Ihr werdet euch denken können was ich als nächstest poste : GuiMaintainer::addClient.

    void GuiMaintainer::addClient( char* clientName) {
         std::string temp ( clientName);
         ClientNames.push_back( temp );
         SendMessage( hClientList[1], LB_ADDSTRING, 0,0);
         SizeListItems( hClientList[1] , ClientNames );
         SendMessage( hClientList[1] , WM_VSCROLL, SB_BOTTOM, 0);
    
    }
    

    Die alleine macht aber eig. garnichts, sondern die Arbeit wird da in der Call-back function erledigt. Die Funktionsweise habe ich von _Falke aus einem anderen Thread. Er hat sie geschrieben, ich sie nur angepasst. Funktioniert eig. auch. Naja, hiermal der relevante Call-Back Teil :

    case WM_DRAWITEM : {
    
                 if( wParam == ClientListBoxId ) {
                     // MessageBox(NULL,"Got smth", "In listbox" , MB_OK );
    
                    LPDRAWITEMSTRUCT lpd = reinterpret_cast<LPDRAWITEMSTRUCT>(lParam);
                    switch (lpd->itemAction) {
                           case ODA_SELECT:
                           case ODA_DRAWENTIRE:
    
                           if( ClientNames.size() < lpd->itemID) { break; } 
                           SetBkMode(lpd->hDC, TRANSPARENT);
                           FillRect(lpd->hDC, &lpd->rcItem,
                                   (lpd->itemState & ODS_SELECTED)? GetSysColorBrush(COLOR_HIGHLIGHT): GetSysColorBrush(COLOR_WINDOW));
    
                           DrawText(lpd->hDC,
                                   ClientNames[lpd->itemID].c_str(),
                                   ClientNames[lpd->itemID].length(),
                                   &lpd->rcItem,
                                   DT_LEFT | DT_WORDBREAK );
                           case ODA_FOCUS:
                                break; 
                           }
                    } 
    
                 // NmbrOfChatWindows ist eine Int-variable in GuiMaintainer.h Sie wird jedes mal erhöht, wenn ein neues Chat-Fenster erstellt wird.
                 // Ich suche hier zunächst erstmal 
                 // Irgendwie glaube ich aber, dass (ChatWindowHandles[i]->output == GetDlgItem( ChatWindowHandles[i]-> mainWindow, wParam ) )  für jedes 
                 // Fenster true ist :/ 
                 for( int i = 0; i < NmbrOfChatWindows; i ++ ) {
    
                      if ( ChatWindowHandles[i]->output == GetDlgItem( ChatWindowHandles[i]-> mainWindow, wParam )   ) {
    
                           LPDRAWITEMSTRUCT lpd = reinterpret_cast<LPDRAWITEMSTRUCT>(lParam);
                           switch (lpd->itemAction) {
                           case ODA_SELECT:
                           case ODA_DRAWENTIRE:
    
                           if(ChatWindowHandles[i]->OutputData.size() < lpd->itemID) { break; } 
                           SetBkMode(lpd->hDC, TRANSPARENT);
                           FillRect(lpd->hDC, &lpd->rcItem,
                                   (lpd->itemState & ODS_SELECTED)? GetSysColorBrush(COLOR_HIGHLIGHT): GetSysColorBrush(COLOR_WINDOW));
    
                           DrawText(lpd->hDC,
                                    ChatWindowHandles[i]->OutputData[lpd->itemID].c_str(),
                                    ChatWindowHandles[i]->OutputData[lpd->itemID].length(),
                                    &lpd->rcItem,
                                    DT_LEFT | DT_WORDBREAK );
                           case ODA_FOCUS:
                                break; 
                           }
                      }
                 }
    
                 break;
    
            }
    

    Puhh ... hier noch GuiMaintainer::SizeListItems()

    void GuiMaintainer::SizeListItems( HWND list, const std::vector<std::string>& Data) {
                       if(!Data.size()) return;
                       HDC hdc = GetDC(list);
                       SelectObject(hdc, reinterpret_cast<HFONT>(SendMessage(list, WM_GETFONT, 0, 0)));
                       bool erasebk = false;
                       for(size_t i=0;i<Data.size();++i) {
                           RECT itm;
                           SendMessage(list, LB_GETITEMRECT,i,reinterpret_cast<LPARAM>(&itm));
                           int h = DrawText(hdc,
                           Data[i].c_str(),
                           Data[i].length(),
                           &itm,
                           DT_LEFT | DT_WORDBREAK | DT_CALCRECT);
                           if(h != SendMessage(list, LB_GETITEMHEIGHT,i,0)) {
                           erasebk  = true; // wenn sich die Höhe eines Eintrags geändert hat, löschen wir den Hintergrund der Liste
                           SendMessage(list, LB_SETITEMHEIGHT, i, h);
                           }
                       }
                       ReleaseDC(list,hdc);
                       InvalidateRect(list, 0, erasebk); 
    }
    

    An dieser Stelle : Schonmal vielen Dank an die, die sich das alles durchgelesen haben ;P Ich sende das Posting jetzt erstmal ab. Wenn Euch Informationen ( Quellcode ) fehlen, dann postet das, werde dann das entsprechende Stück posten.

    Mfg !



  • Ich habe zwar keine Lust mir das jetzt alles durchzulesen, aber hast du eigentlich mal versuchst das ganze mit nem Debugger laufen zu lassen? Wenn dein Fenster abstürzt, scheint dein Programm irgendwo zu hängen, somit können die Nachrichten nicht mehr verarbeitet werden. Du musst jetzt nur rausfinden wo. Debugger wäre eine (gute(!)) Möglichkeit, sonst halt mit MessageBox() 😃



  • xD Ja also ich kann leider so garnicht mit dem Debugger umgehen, aber per MessageBoxe's hab ich folgendes rausgefunden : Aufhängen tut sich das Programm an der stelle :

    if( wParam == ClientListBoxId ) {
                     // MessageBox(NULL,"Got smth", "In listbox" , MB_OK );
    
                    LPDRAWITEMSTRUCT lpd = reinterpret_cast<LPDRAWITEMSTRUCT>(lParam);
                    switch (lpd->itemAction) {
                           case ODA_SELECT:
                           case ODA_DRAWENTIRE:
    
                           if( ClientNames.size() < lpd->itemID) { break; }
                           SetBkMode(lpd->hDC, TRANSPARENT);
                           FillRect(lpd->hDC, &lpd->rcItem,
                                   (lpd->itemState & ODS_SELECTED)? GetSysColorBrush(COLOR_HIGHLIGHT): GetSysColorBrush(COLOR_WINDOW));
    
                           DrawText(lpd->hDC,
                                   ClientNames[lpd->itemID].c_str(),
                                   ClientNames[lpd->itemID].length(),
                                   &lpd->rcItem,
                                   DT_LEFT | DT_WORDBREAK );
                           case ODA_FOCUS:
                                break;
                           }
                    }
    

    Denn die auskommentierte MessageBox erscheint, und nach Klick auf "Ok" -> "Blabla funktioniert nicht mehr ... ".



  • Du musst den Befehl der das Problem verursacht schon genau feststellen.
    Das Switch wirds wohl nicht sein 😉



  • ;P Um genau zu sein, hängt sich das Ding direkt nachdem ich es erzeuge auf -.- Ich hab die gesammte Callback function etc. auskommentiert.

    Funktion zum erzeugen :

    BOOL GuiMaintainer::createClientList() {
    
         WNDCLASSEX windowClass;
         char szClassName[] = "ClientList";
         windowClass.hInstance = Inst;
         windowClass.lpszClassName = szClassName;
         windowClass.lpfnWndProc = WindowProcedure;      
         windowClass.style = CS_DBLCLKS;                 
         windowClass.cbSize = sizeof (WNDCLASSEX);
    
          /* Use default icon and mouse-pointer */
         windowClass.hIcon = LoadIcon (NULL, IDI_APPLICATION);
         windowClass.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
         windowClass.hCursor = LoadCursor (NULL, IDC_ARROW);
         windowClass.lpszMenuName = NULL;                 
         windowClass.cbClsExtra = 0;                     
         windowClass.cbWndExtra = 0; 
    
         windowClass.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
         if( !RegisterClassEx(&windowClass) ) {
             MessageBox(NULL,"CreateClientList() failed ! ", "Error" , MB_OK );
             return FALSE;
         }
    
         hClientList[0] = CreateWindowEx (
               0,
               szClassName,         
               "List of Chatpartner",       
               WS_VISIBLE | WS_OVERLAPPED | WS_SYSMENU ,
               CW_USEDEFAULT,       
               CW_USEDEFAULT,       
               150,                 
               350,                 
               hLoginHandles[MAINWINDOW],        
               NULL,                
               Inst,       
               NULL                 
               ); 
    
         hClientList[1] = CreateWindowEx (
               WS_EX_CLIENTEDGE,                   
               "LISTBOX",         
               "",       
               WS_VISIBLE | WS_CHILD | WS_VSCROLL | LBS_OWNERDRAWVARIABLE | LBS_NOTIFY | LBS_NODATA  , /
               3,       
               3,       
               130,                 
               309,                 
               hClientList[MAINWINDOW],        
               (HMENU)ClientListBoxId,                
               Inst,       
               NULL                 
               );
    
    }
    

    Sie wird erzeugt -> Hängt sich auf. Komisch.



  • So, ich korrigiere mich noch einmal :

    Das Fenster an sich funktioniert einwandfrei. Ich habe es testweise einfach mal so erzeugt und ich konnt es verschieben etc. ohne Probleme. Hat sich nicht aufgehängt.

    Aber sobald der Client zum Server eine Verbindung erfolgreich herstellt, hängt sich das Fenster komplett auf.

    while (1)
        {
            while( PeekMessage (&messages, NULL, 0, 0, PM_REMOVE ) ) {
                   if( messages.message == WM_QUIT )
                      return TRUE;
                   /* Translate virtual-key messages into character messages */
                   TranslateMessage(&messages);
                   /* Send message to WindowProcedure */
                   DispatchMessage(&messages);
            }
    
               if( !( gui.getConnecterClass().getSocket() == INVALID_SOCKET ) ) {
                   MessageBox(NULL,"Erfüllt ;=) ", "SUX", MB_OK);
                  FD_SET(gui.getConnecterClass().getSocket(), &set );
                  int rc = select(0,&set,NULL,NULL,NULL);
                  if( rc == SOCKET_ERROR) {
                     MessageBox( NULL , "Select failed ... Closing the programm", "Error" , MB_OK );
                     return 1;
                  }
                  if ( FD_ISSET( gui.getConnecterClass().getSocket() , &set ) ) {
                      char buf[1024];
                      // Usually the Server sends the Name of the Client first.
                      // This is not the case if he sends some kind of special message, thus we have to check if it is one
                      rc = recv ( gui.getConnecterClass().getSocket() , buf , sizeof(buf) , 0 );
                      if ( rc == 0 || rc == SOCKET_ERROR ) {
                          MessageBox(NULL,"SOCKET_ERROR","SOCKET_ERROR" , MB_OK);
                          // gui.TerminateClients();
                          return 0;
                      }
    
                      buf[rc] = 0;
                      // That happens here 
                      if( strcmp( buf , "\r\r####NEW CLIENT CONNECTED####\r\r" ) == 0 ) {
                         MessageBox(NULL,"Special messges OoOooo", "OoooooO", MB_OK);
                         memset( buf , 0 , sizeof(buf ) );
                         rc = recv ( gui.getConnecterClass().getSocket() , buf , sizeof(buf) , 0 );
                         buf[rc] = 0;
                         MessageBox(NULL, buf , "Got smth", MB_OK);
                         // gui.addClient( buf );
                      }
                      else {
                         // If the server did not send any special message, then it simply send the Client's name first
                         // Now he sends the Clients input next, which we are going to store in another buffer
                         char buf2[1024];
                         MessageBox(NULL,"NORMAL message OoooOOOoo", "OoOooOOO", MB_OK);
                         rc = recv ( gui.getConnecterClass().getSocket() , buf2, sizeof(buf2) , 0 );
                         // buf2 contains the textt o be updated, buf contains the clients name. And that's exactly what UpdateWindowOutput
                         // needs as information 
                         // gui.UpdateWindowOutput( buf2 , buf );
                      }
                  }   
               }
            }
    

    Wie man sieht : Ich habe alles, was irgendwelche Funktionen von GuiMaintainer callt auskommentiert. TROTZDEM hängt es sich noch auf ! Sobald eine Verbindung steht, und somit if( !( gui.getConnecterClass().getSocket() == INVALID_SOCKET ) erfüllt ist, hängt das Fenster sich auf. Ich schlaf jetzt mal drüber, villeicht finde ich morgen irgendwo die Logik ...



  • An deiner Stelle würde ich mir mit dem Debugger mal ansehen, wo innerhalb dieser Funktion dein Programm so lange zu tun hat, daß es keine Zeit mehr hat, auf Windows-Nachrichten zu reagieren.



  • Ok gut, dann werde ich es morgen nochmal in VSC++ compilen, denn Dev C++ hat keinen Debugger ( zmnd meint das Programm immer, dass es irgendwie nicht funktioniert ) .

    Soweit ich es mit MessageBoxen überprüft habe, hängt es sich genau hier auf :

    int rc = select(0 ,&set, NULL , NULL, NULL);
    

    Also beim select Aufruf. Unlogisch. Der Aufruf wird nie beendet, denn die MessageBox direkt nach dem Aufruf erscheint nie. Finde ich komisch, eig. sollte select doch einfach einen 0 returnen, wenn es failt, und nicht sich aufhängen.

    Auf jeden Fall mal danke schonmal fürs Lesen & Posten 😉



  • Du hast select() noch nicht ganz verstanden. Letzter Parameter ist ein Timeout 😉

    Edit:

    Dev C++

    Böse!



  • Ich habe Deinen Code nur überflogen, aber Deine Methode vom Socket zu lesen ist fehleranfällig.
    Szenario: Der Server sendet 500Byte an Deinen Client. Dein Client macht

    rc = recv ( gui.getConnecterClass().getSocket() , buf , sizeof(buf) , 0 );
    

    Wenn ich Deinen weiteren Code richtig verstehe, verlässt Du Dich darauf, dass Dein Client jetzt 500Byte gelesen hat (falls es nicht so ist, vergiss dieses Posting). Das darfst Du aber nicht. Es wäre denkbar, dass Du mehrmals recv aufrufen musst, um die gesamte Nachricht des Servers zu bekommen. Mal angenommen, Du bekommst bei Deinem recv nur 20Byte, dann würde mindestens schon mal der Vergleich mit Deiner Spezialnachricht (neuer Client) fehlschlagen ... - Wieso codierst Du die Art der Nachricht nicht einfach durch Nummern, 1 = normaler Text, 2 = neuer Client, 3 = ein anderer Client hat sich abgemeldet ...?
    Auf jeden Fall empfiehlt es sich, sich nicht darauf zu verlassen, dass recv irgendeine bestimmte Anzahl von Bytes liest.
    Besser ist es, über den sendenden Socket zuerst die Anzahl der Bytes der folgenden Nachricht zu schicken, dann die Nachricht, und beim empfangenden Socket zuerst die Größe der folgenden Nachricht zu lesen und dann in einer Schleife so lange recv aufzurufen, bis man die ganze Nachricht empfangen hat.



  • Mal sehen, was die MSDN dazu sagt:

    The select function determines the status of one or more sockets, waiting if necessary, to perform synchronous I/O.

    timeout [in] (Anm: der letzte Parameter)
    The maximum time for select to wait, provided in the form of a TIMEVAL structure. Set the timeout parameter to null for blocking operations.

    The parameter time-out controls how long the select can take to complete. If time-out is a null pointer, select will block indefinitely until at least one descriptor meets the specified criteria.

    Um das mal zusammenzufassen - select() wartet so lange, bis auf dem überwachten Socket tatsächlich etwas geschieht. Und da du die ganze Aktion in deiner Window-Prozedur hast, kann das Fenster in der Zwischenzeit nicht auf Eingaben von außen reagieren.



  • Hmmmm schrieb:

    int rc = select(0 ,&set, NULL , NULL, NULL);
    

    Also beim select Aufruf. Unlogisch. Der Aufruf wird nie beendet, denn die MessageBox direkt nach dem Aufruf erscheint nie.

    Mit dem TimeOut - Parameter NULL wartet Dein select so lange, bis es was zu Lesen gibt. Also entweder bekommt Dein Client hier keine Nachricht vom Server, oder Dein set ist nicht korrekt befüllt ...



  • CreateThread ist wohl der Versuch, C++ Code möglichst unportabel und hässlich zu machen, stimmts?
    VC++ 2010 würde dir unter Windows std::thread anbieten, aus dem neuen Standard. Alternativ boost::thread.

    Damit kannst du portabel, sachön und sauber deine Threads starten ...



  • Ethon schrieb:

    VC++ 2010 würde dir unter Windows std::thread anbieten, aus dem neuen Standard.

    Leider nein...



  • Achso okay ;P Erstmal danke für die Erleuchtung. Eigentlich würde das das Problem natürlich erklären, aber auch so :

    int rc = select(0 ,&set, NULL , NULL, 0 );
    

    blockiert der select()-call immernoch. Soweit ich gelesen habe sollte select() sofort returnen, falls 0 als letzter Parameter übergeben wird ?
    Und ja, DevC++ ist veraltet ;P Ich mag die IDE an sich aber und ich dachte, dass für ein eher kleines Projekt Dev C++ erstmal reicht. Aber ich sollte echt entweder auf VSC++, Code::Blocks oder wxDevC++ umsteigen. Werde ich auch in näherer Zukunft tuen.

    @ Belli : Ich werde wohl ein Limit für die Eingabe-Fenster der Clients setzten, sodass die 1024 buf auf jeden Fall groß genug ist. Momentan ist das in der Tat ein Fehler, aber nur weil ich mich damit noch nicht genauer beschäftigt habe. Ich wollte erstmal die groben sachen zum Laufen bringen bevor ich die feineren Dinge implementiere ;P
    Bei dem Vergleich mit der Special-message, der fehlschlägt wenn der Client <20 bytes schickt hast du natürlich recht, habe ich nicht bedacht ;P Da werde ich mich noch drum kümmern müssen, danke ;P
    Das Problem bei normalen Zahlen als Special-message ist, dass solche Zahlen auch sehr wahrscheinlich von normalen Usern eingegeben werden können. Die special-messages mit \r\r\n nicht.

    @ CStoll : Blockiert jetzt select() IMMER oder nicht, wenn ich 0 übergebe ?

    @ Eton Naja, das ist im Prinzip natürlich richtig, aber : Bei meinem Code wird ein nicht verwendeter CreatThread call auch nichts mehr an Portabilität verbessern können. Es ist im Moment zu 100% Windows-dependent, da ich komplett die WinApi verwende. Wenn ich an boost denke, kommen mir nurnoch endlose Stunden in den Sinn, in denen ich versuch habe es die gedownloadeten files richtig zu setzten etc. damit es klappt. Hab's dann nach ein paar Stunden einfach aufgeben.
    Hauptsächlich schreibe ich ja eh eher zur Übung um einfach bisschen Praxis zu haben etc.
    Was genau ist daran eig. so hässlich ? Der C-Style von WinApi ?

    Mfg


Anmelden zum Antworten