CreateThread - keine Memberfunktion erlaubt ?



  • 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



  • Hmmmm schrieb:

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

    Habe ich doch gesagt - wenn du dort NULL (oder 0, das ist identisch) übergibst, wartet select() unbegrenzt, d.h. bis tatsächlich etwas passiert. Bau dir lieber ein TIMEVAL zusammen, das du dort übergeben kannst (und werte den Rückgabewert aus, um zu erkennen, ob es etwas zu verarbeiten gibt).



  • Oh, da hab ich das falsch verstanden. Dachte wenn man den Parameter auf 0 setzt, aber eig. natürlich wenn man das entsprechende Member von dem struct auf 0 setzt. Jop, so funktioniert das auch 🙂 Thx soweit erstmal an alle. Es werden sicher noch weitere Probleme auftauchen 😉



  • Hmmmm schrieb:

    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.

    Das ist eigentlich kein Problem. Du musst nur JEDE Message mit so einer Qualifikation beginnen lassen. Dann weißt Du am anderen Ende, dass das erste Zeichen eine Kennung dafür ist, was für eine Information (Text, oder eine besondere Special Message) folgt. Wenn nun ein User "2Hallo da" eingibt, dann schickst Du ja "12Hallo da" über die Leitung - angenommen, die 1 steht in Deinem System für normale UserMessage. Du liest dann das erste Zeichen, weißt, aha, der Rest ist die Eingabe eines Users ... usw.



  • Ahhh, das ist ne echt gute Idee 😉 So werd ich's wohl machen, wird ein paar strcpy's und cat's mehr sein, aber dafür sicherer. Danke für die Idee !



  • So, wieder ein neues Problem 😛

    Folgendes : Ich möchte in einer bestimtmen Listbox Double-Klicks abfangen und dann den doppelgeklickten Eintrag verabeiten ( konrekt : Er soll zu dem geklickten Namen connecten bzw mit ihm chatten können ). Aber irgendwie klappt das mit dem Text von dem bestimmten Index kriegen nicht. Mein buffer bleibt immer leer ;O

    if( HIWORD(wParam) == LBN_DBLCLK )  { 
       int index = SendMessage( (HWND)lParam  , LB_GETCURSEL , 0 , 0 );
    
       char buffer[400];
       SendMessage( (HWND)lParam , LB_GETTEXT , index , (LPARAM)buffer );
       if ( strcmp( buffer , "Group Chat" ) != 0 ) // Sollte ich glaube ich noch ändern, da gefährlich falls der geklickte Name < "Group Chat" ist, richtig ? 
          createChatWindow( buffer );
       else
          createGroupChatWindow();   
    } break;
    

    Jemand ne Idee ?



  • Bin mir gerade nicht 100%ig sicher, aber das strcmp kannste schon so lassen denke ich.

    Was du allerdings ändern solltest:
    Wer garantiert dir, dass der Name nicht über 399 Zeichen lang ist? Siehe LB_GETTEXTLEN.

    Ansonsten fällt mir gerade nichts auf, was meinst du genau mit "Mein buffer bleibt immer leer"? So wie er vorher war? Steht in buffer[0] dann '\0'?



  • @ strcmp Ahh gut, umso besser 😉

    Für die Namen werde ich sehr wahrscheinlich einen Limit auf 50 bytes oder so setzen, also ist das kein Problem.

    Jop, ich habe mir den buffer in ner Messagebox ausgeben lassen

    MessageBox(NULL,buffer ,"BLA",MB_OK);
    

    und diese ist einfach leer. Genauso wie der Titel des Fensters das ich danach erzeuge, welches eig. den übergeben String als Titel haben sollte.
    Einen Eintrag in die Listbox habe ich natürlich hinzugefügt, der Index wird auch richtig bestimmt.



  • http://msdn.microsoft.com/en-us/library/bb761313(v=vs.85).aspx schrieb:

    If the list box has an owner-drawn style but not the LBS_HASSTRINGS style, the buffer pointed to by the lParam parameter receives the value associated with the item (the item data).

    Das Problem kann ausgeschlossen werden?

    Der nächste Schritt wäre jetzt mal den Rückgabewert von

    Hmmmm schrieb:

    SendMessage( (HWND)lParam , LB_GETTEXT , index , (LPARAM)buffer );

    auszuwerten. Ist der irgendeine Zahl? Oder 0? Oder LB_ERR?

    Woher weist du, dass der Index richtig bestimmt wird? Ich rate dir wirklich dazu, den Umgang mit dem Visual Studio Debugger zu lernen. Das ist nicht schwer, aber hilft enorm 😉

    Edit:

    Hmmmm schrieb:

    Für die Namen werde ich sehr wahrscheinlich einen Limit auf 50 bytes oder so setzen, also ist das kein Problem.

    Dann lege dafür eine Konstante fest. Je nach C oder C++

    #define NICKNAME_MAX_LEN 50 // C
    static const int nickname_max_len = 50 // C++
    

    Falls du dich für C++ entscheidest, solltest du außerdem auch C++ Casts verwenden. (static_cast<>, reinterpret_cast<>, etc.)



  • Oh, dein Quote hat mich an folgendes errinert ;P : Habe für die Listbox LBS_NODATA als flag, denn ich speichere die Namen bzw alles was diese Listbox je enthalten wird in einem vector.
    Somit kann ich einfach den Namen aus dem Vector suchen eben mit meiner "index" variable als index ;P Klappt so auch.

    Ja, sollte ich warsch. echt mal tuen ;P Habe es per MessageBox überprüft ( itoa + Messagebox ).

    Jetzt muss ich nurnoch rausfinden, wie ich a) Einträge aus meiner Listbox entferne ( sodass die Einträge auch "nachrutschen" ) und b) bestimmte Namen aus dem vector entferne und dafür sorge, dass die einträge "nachrutschen". Sollte aber nicht zu wild sein ( zmnd b) ).

    Zu den C++ casts werde ich mich auch mal überreden, finde die nur ätzender zu schreiben ;P Außerdem weiss ich nie so recht, ob ich static_cast oder reinterpret_cast oder w/e nehmen soll ... Muss ich mich auch nochmal zu belesen 😉

    Btw : Wieso #define in C und const in C++ ? const geht wohl auch in C ? Und wieso genau static ?



  • Hmmmm schrieb:

    Btw : Wieso #define in C und const in C++ ? const geht wohl auch in C ? Und wieso genau static ?

    Das ist für die örtliche Style Polizei 😉

    static const x ist #define vorzuziehen, da es Typsicher ist. Soll heißen:

    static const int val = "hallo!";
    

    Wird einen Fehler verursachen. In C ist/war

    static const int val = 50;
    char test[val];
    

    aber nicht möglich. Deswegen nimmt man dort immer noch #define.



  • Oke 😉 Klingt plausibel ^^

    Mal ne andere Frage, eher nicht zu WinApi, sonder C++ : Wtf soll man mit diesen Iteratoren anfangen ? Also zB :

    std::vector < std::string >::iterator pos;
    pos = find ( clientNames.begin() , clientNames.end() , clientName );
    if ( pos != clientNames.end() ) {
       // do something
    }
    else {
       // do it not
    }
    

    Eigentlich wollte ich den Index von irgendeinem Client-Namen rausfinden, aber mit diesem iteratoren-typ kann ich garnichts anfangen. Lässt sich ja schliesslich nicht nach int casten oder sonstiges. Keine Ahnung wie ich das also mache ;P



  • Iteratoren haben den Vorteil, daß du damit auf beliebigen Datenstrukturen arbeiten kannst - der Zugriff auf eine verkettete Liste (std::list<>) funktioniert genauso wie auf ein dynamisches Array (std::vector<>) oder einen Binärbaum (std::set<>/std::map<>). Mit dem gefundenen Iterator kannst du jetzt weiterarbeiten wie mit einem Zeiger - du dereferenzierst ihn, um an die dahinterstehenden Daten zu gelangen

    Wenn du den Index des gefundenen Elements heranwillst, kannst du std::distance() verwenden (für vector<> geht auch die Iterator-Differenz):

    std::vector < std::string >::iterator pos;
    pos = find ( clientNames.begin() , clientNames.end() , clientName );
    if ( pos != clientNames.end() ) {
       int index = std::distance(clientNames.begin(), pos);
       // oder index = pos-clientNames.begin();
    }
    else {
       // do it not
    }
    

    PS: Aber ich würde im Ernstfall keine parallelen Arrays verwenden, sondern alle Informationen zu einem Client in eine Struktur zusammenfassen und dann einen std::vector<ClientData> verwenden.



  • Achso, das ist natürlich praktisch, danke !

    Noch eine Frage : Was passiert in dem Fall, wenn das gesuchte Element an der letzten Stelle ist ? Würde das dann nicht fehlschlagen. Denn eig. ist die bedingung if ( pos != clientNames.end() ) nicht erfüllt, aber das Element wurde trotzdem gefunden.

    Zu dem gesamt-struct thema : Hmm, wäre das denn besser ? Dann müsste ich immer ewig oft den '.' operator benutzten a la ClientStruct.clientList.ClientNames[index].c_str(); Wie das aussieht 😉



  • Hmmmm schrieb:

    Noch eine Frage : Was passiert in dem Fall, wenn das gesuchte Element an der letzten Stelle ist ? Würde das dann nicht fehlschlagen. Denn eig. ist die bedingung if ( pos != clientNames.end() ) nicht erfüllt, aber das Element wurde trotzdem gefunden.

    clientNames.end() zeigt nicht auf das letzte Element deiner Liste, sondern hinter das letzte Element.

    Zu dem gesamt-struct thema : Hmm, wäre das denn besser ? Dann müsste ich immer ewig oft den '.' operator benutzten a la ClientStruct.clientList.ClientNames[index].c_str(); Wie das aussieht 😉

    Viel länger als mit den parallelen Arrays wird der Code dadurch nicht, eher im Gegenteil. Vergleich mal folgendes:

    pos = find(ClientNames.begin(),clientNames.end(),name);
    if(pos!=ClientNames.end())
    {
      int index = distance(clientNames.begin(),pos);
      return ClientIPs[index];
    }
    
    // vs.
    
    pos = find_if(Clients.begin(),Clients.end(),compare_name(name));
    if(pos!=Clients.end())
    {
      return pos->IP;
    }
    

    (außerdem mußt du nicht bei jedem Login und Logout darauf achten, daß die wirklich alle Client-Daten-Arrays synchron zueinander änderst)


Anmelden zum Antworten