Boost.Thread & Winsock -> Programmabsturz



  • Ich versuche mich gerade an einem kleinen IRC Client, der gleichzeitig senden und empfangen soll. Stellt es euch vor wie im Sinne von mIRC. Als grobe Anleitung hat mir folgendes Tutorial gedient: Tutorials.de/.../
    Wenn ich einfach das ganze Threading Zeugs rauslasse, funktioniert das Programm, doch wenn ich versuche mittels der Boost Bibliothek dem Programm Threads einzuhauchen, stürzt es wundersamer Weise (liegt wohl eher an meinem Laienwissen) ab.
    Die Dokumentation von Boost.Thread ist meiner Meinung nach auch nicht die beste...

    Auf jeden Fall habe ich in meinem Programm 2 "Endlosschleifen", eine zum Empfangen, die andere zum Senden von Daten an einen beliebigen IRC-Server. Dafür wollte ich 2 Threads gebrauchen. Und da mir Boost so gut gefällt, dacht ich, ich probiere es damit.

    Hier mal der Source von der main.cpp:

    #include <iostream>
    #include <boost/thread.hpp>
    #include "irc.hpp"
    
    // globals
    bool runThread = true;
    boost::mutex mMutex;
    
    // protos
    void recvProc(Jeix::IRC::Server&);
    
    int main()
    {
    	std::cout << "Jeix::IRC::Server test\n";
    	try
    	{
    		Jeix::IRC::Server seilen;
    		boost::thread recvThread(boost::bind(&recvProc, boost::ref<Jeix::IRC::Server>(seilen)));
    
    		seilen.Connect("irc.seilen.de", 6667);
    		seilen.Login("diggllet", "myhost", "irc.seilen.org", "max fischer");
    
    		char input[1024];
    		::std::string strInput;
    		while(1)
    		{
    			std::cin.getline(input, 1024);
    			strInput = input;
    			if(!strInput.compare("/exit"))
    			{
    				boost::unique_lock<boost::mutex> lock(mMutex);
    				runThread = false;
    				break;
    			}
    			strInput += "\r\n";
    
    			seilen.Send(strInput);
    		} // while
    
    		recvThread.join();
    	}
    	catch(std::string& ex)
    	{
    		std::cout << ex << std::endl;
    	}
    
    	//PAUSE;
    	std::cin.clear();
    	std::cin.ignore();
    	std::cin.get();
    }
    
    void recvProc(Jeix::IRC::Server& serv)
    {
    	while(1)
    	{
    		boost::unique_lock<boost::mutex> lock(mMutex);
    		std::cout << serv.Receive();
    		std::cout << std::endl;
    
    		if(runThread)
    		{
    			break;
    		}
    	}
    }
    

    Sorry erstmal für den doch relativ langen Quelltext, doch erschien mir der größte Teil nun doch wichtig.
    Hier noch die Implementation der Server.Receive() Methode. Falls ihr noch weiteres wissen wollt bzw. wichtig ist, sagt es bitte einfach.

    ::std::string Server::Receive()
    	{
    		::boost::mutex mTemp;
    		::boost::unique_lock<::boost::mutex> lock(mTemp);
    		char output[2000]; //declared in srv.hpp
    		int countBytes = 0; //	 declared in srv.hpp
    
    		countBytes = recv(sock, output, sizeof(output), 0);
    		if (countBytes == SOCKET_ERROR)
    		{	// Error
    			throw "Error, could not receive response from server!";
    		}
    		if (countBytes > 0)
    		{
    			output[countBytes] = 0;	// recv doesn't put an \0 at the end of the string
    			::std::string temp(output);
    			return temp;
    		}
    		return "";
    	}
    

    Bei der Zeile "if (countBytes == SOCKET_ERROR)" scheint es wohl irgendwie zu haken. Hier ein Screenshot von der Fehlermeldung:
    http://img521.imageshack.us/my.php?image=debugerrpx2.png

    Ich hab mir das mit dem Boost.Thread größtenteils zusammengeraten, wenn ich ehrlich sein soll, da die Dokumentation in meinen Augen sehr mangelhaft ist und das einzige Beispiel, was ich finden konnte, war von 2002.

    Vielleicht hab ich auch schon von Anfang an den falschen Ansatz, ich bin noch nicht so erfahren, danke für eure Hilfe!

    MfG issen1



  • hmm ich weiß ja nicht aber du fängst die exception völlig falsch. Deswegen kommt auch die Meldung, da du die exception eben gar nicht auffängst:

    catch(std::string& ex)
    {
            std::cout << ex << std::endl;
    }
    //Das nüsste aber so sein:
    catch(std::exception& ex)
    {
            std::cout << ex.what() << std::endl;
    }
    

    Probiers mal aus, dann siehste wenigsten was da worklich fliegt.
    Gruß



  • Aber er wirft doch einen std::string...



  • Fehler schrieb:

    hmm ich weiß ja nicht aber du fängst die exception völlig falsch. Deswegen kommt auch die Meldung, da du die exception eben gar nicht auffängst:

    catch(std::string& ex)
    {
            std::cout << ex << std::endl;
    }
    //Das nüsste aber so sein:
    catch(std::exception& ex)
    {
            std::cout << ex.what() << std::endl;
    }
    

    Probiers mal aus, dann siehste wenigsten was da worklich fliegt.
    Gruß

    WildUmherWerfer schrieb:

    Aber er wirft doch einen std::string...

    Nein! Es wird ein const char* geworfen. Das ist KEIN std::string.
    Desshalb wird da nichts gefangen.
    Simon


  • Administrator

    Glaube nicht, dass dies nur an Boost.Thread liegt. Die Dokumentation ist knapp und setzt halt ein Wissen in Threads voraus. Wenn ich deinen Quellcode anschaue, scheint das aber bei dir nicht vorhanden zu sein.

    boost::thread recvThread(boost::bind(&recvProc, boost::ref(seilen))); // <Jeix::IRC::Server> nach boost::ref ist unnötig, das findet der Compiler selber raus. (1)
    
    seilen.Connect("irc.seilen.de", 6667); (2)
    

    Was passiert hier?
    (1) Der Receiver Thread startet ... Wahrscheinlich wird er sofort versuchen Daten zu empfangen, du hinderst ihn ja nicht daran, gibt wahrscheinlich ein SOCKET_ERROR, da sich der Client noch mit keinem Server verbunden hat.
    (2) Nun kommt der erste Thread womöglich hier hin, und wird probieren sich zu verbinden. Das könnte sogar erfolgreich gehen.

    Du musst diesen Teil auf jedenfall auch synchronisieren oder gar noch besser, den zweiten Thread erst nach erfoglreichem Connect und Login starten!

    throw "Error, could not receive response from server!";
    

    Zwei Fehler hier!
    Diese Exception wird nirgend gefangen. Bei (1) startest du einen neuen Thread, der wird keine Exception in irgendeinen anderen Thread hineinwerfen. Diese Exception wird also ganz sicher nicht gefangen. Wird wohl zu einem terminate im anderen Thread führen und wahrscheinlich zum Absturz des Programmes.

    Du hast ein catch im ersten Thread, welches aber diese Exception ganz sicher nicht fangen wird, auch wenn die Exception über Threadgrenzen geworfen würde, was aber nicht der Fall ist:

    catch(std::string& ex)
    

    std::string ist != char const*
    Wäre mir zumindest neu, dass diese implizite Konvertierung bei Exceptions funktioniert.

    ::boost::mutex mTemp;
    ::boost::unique_lock<::boost::mutex> lock(mTemp);
    

    Was das nützen soll, ist mir ein völliges Rätsel. Jeder Thread wird sein eigenes Mutex erstellen und dieses locken. Eine Synchronisation wird nie stattfinden.

    Also, wenn ich mir das erlauben darf, lies vielleicht zuerst einmal ein Tutorial zu Threads durch, bevor du dich mit Boost.Thread beschäftigst. Zudem wäre auch ein Tutorial zu Exceptions und Templates wohl nicht all zu schlecht.

    Grüssli



  • Dravere schrieb:

    Glaube nicht, dass dies nur an Boost.Thread liegt. Die Dokumentation ist knapp und setzt halt ein Wissen in Threads voraus. Wenn ich deinen Quellcode anschaue, scheint das aber bei dir nicht vorhanden zu sein.

    Du hast natürlich Recht, mein Wissen über Threads bzw. Threading ist sehr oberflächlich. Vielleicht könntest du mir eine elektronische Lektüre empfehlen?

    Du hast ein catch im ersten Thread, welches aber diese Exception ganz sicher nicht fangen wird, auch wenn die Exception über Threadgrenzen geworfen würde, was aber nicht der Fall ist:

    catch(std::string& ex)
    

    std::string ist != char const*
    Wäre mir zumindest neu, dass diese implizite Konvertierung bei Exceptions funktioniert.

    Ah, danke für den Hinweis (auch an die anderen Poster 😉 ). Da war mir ein Denkfehler unterlaufen. Das passiert mir des öfteren, besonders in der Woche, wenn es einem an Schlafdefiziten doch wirklich nicht mangelt. 😃

    ::boost::mutex mTemp;
    ::boost::unique_lock<::boost::mutex> lock(mTemp);
    

    Was das nützen soll, ist mir ein völliges Rätsel. Jeder Thread wird sein eigenes Mutex erstellen und dieses locken. Eine Synchronisation wird nie stattfinden.

    Das war nur einer von vielen Versuchen, die ich unternommen habe, damit es endlich funktioniert. Ich hätte da noch eine Frage:

    boost::unique_lock<boost::mutex> lock(mMutex);
    std::cout << serv.Receive();
    

    Wenn ich das so programmiere, wird dann jedes Objekt bzw. jede Variable, die genutzt wird gelocked? Denn ich bekomme neuerdings immer diesen "WSANOTINITIALISED 10093" Fehlercode, wenn ich versuche zu receiven oder zu senden. Da WSAStartup() jedoch eigentlich erfolgreich war, genau wie die Connect() Methode, erschließt sich mir der Sinn nicht ganz. Außer, dass halt folgendes eintritt:

    The application may be accessing a socket that the current active task does not own (that is, trying to share a socket between tasks), or WSACleanup has been called too many times.

    Auf jeden Fall, danke für eure Antworten, denn somit funktioniert mein kleines Programm erstmal (teilweise)! 🙂

    Also, wenn ich mir das erlauben darf, lies vielleicht zuerst einmal ein Tutorial zu Threads durch, bevor du dich mit Boost.Thread beschäftigst. Zudem wäre auch ein Tutorial zu Exceptions und Templates wohl nicht all zu schlecht.

    Grüssli

    Natürlich darfst du. Kritik seh ich gerne, solange sie angemessen formuliert ist. Wie ich oben bereits sagt, wäre es schön, wenn mir einer einen (guten) Artikel über Threading nennen könnte. Ansonsten bin ich mit Exceptions eigentlich ganz gut vertraut, mit Templates habe ich mich noch nicht beschäftigt - außer, dass ich sie verwendet hab bei zB. std::vector; wird aber sicher noch kommen. Spätestens wenn der C++ Primer endlich bei mir zu Hause im Regal steht!

    Beste Grüße
    - issen.


Anmelden zum Antworten