Problem mit recv und endlos schleife



  • sdfsdfsdf:
    Deine Argumente sind nicht unbedingt falsch aber, dieses Bashing C vs C++ Grabenkriege finde ich anstregend. Ich habe mich ganz bewusst für eine manuelle Speicherverwaltung entschieden. Und gleich beleidigend zu werden halte für ich keine gute Option. Und es ist natürlich sicherer die C++ Funktionen zu verwenden aber das Debugging erleichtert es nicht unbedingt.

    Es gibt für alles ein für und wieder. Und eine Tatsache kann man nicht von tisch Wischen C++ ist ein um die Objektorientierung und paar Annehmlichkeiten erweitertes C. Was besonders in den Compilern merken, dar läuft es meistens so ab C++ -> C -> Asm. Nun kann man in bestimmten Situation besonders wenn es darum Leistung zu optimieren einen schritt weglassen.

    Dieser schritt ist auch nur logisch dar man C Funktionen auch in C++ verwenden kann. Ich weiß C99 und der alte C++ Standart sind ein Problem. Und gewisse C++ Funktionen werden direkt in ASM gewandelt aber im großen ganzen ist es so.

    Wichtig ist es einfach auch zu wissen was grob im Background passiert.



  • Tuxist schrieb:

    würde gehen aber das ding muss auch wieder kleiner werden

    Na und? Dann mach ihn halt kleiner.

    Tuxist schrieb:

    wenn ich ein String nehme kann ich keinen memmove oder ähnliches machen beim parsen der Daten

    Brauchst du auch nicht.

    Tuxist schrieb:

    das würde bedeuten das ist mit find und substr arbeiten müsste was sehr viel last erzeugt 😃

    Das liegt nicht am std::string sondern an deiner beschissenen Methode das Zeug zu parsen.

    Tuxist schrieb:

    lighttpd ist in c geschrieben
    apache ist in c geschrieben
    minihttpd ist in c geschrieben
    nynx ist in c++ geschrieben .....

    Na und?

    Tuxist schrieb:

    Das hat schon einen Grund

    Ja, wiederverwendung von altem Code. Das hat aber nichts damit zu tun, ob nun C oder C++ flotter ist.

    Tuxist schrieb:

    zu mal der code eh c clastig.. pthread.. sockets.. fork.. .

    Nur weil du eine C-API benutzt, muss dein Code nicht C-ig sein.

    Tuxist schrieb:

    Und warum sollte man nicht noch die vorteile von cpp nutzen und die nachteile umgehen.

    Was für ne unglaublich dumme Aussage. Tut man doch. Und die Nachteile darfst du mir jetzt ruhig nennen. Ich wette, das Problem bist wieder du.

    Tuxist schrieb:

    schon mal doom3 quellcode angesehen ?

    Ja, und weiter?

    Tuxist schrieb:

    da wirst du das gleiche festellen.

    Was werde ich feststellen?

    Tuxist schrieb:

    oder in tinyxml ?

    Was ist denn damit? Klär mich auf, du Intelligenzbestie.



  • Tuxist schrieb:

    Deine Argumente sind nicht unbedingt falsch

    Nun gib halt zu, dass du Unrecht hast, du Pfeife.

    Tuxist schrieb:

    dieses Bashing C vs C++ Grabenkriege finde ich anstregend.

    Wir auch. Deine schlecht verständlichen Posts übrigens auch.

    Tuxist schrieb:

    Ich habe mich ganz bewusst für eine manuelle Speicherverwaltung entschieden.

    Automatische Speicherverwaltung hat keinen Overhead.

    Tuxist schrieb:

    Und gleich beleidigend zu werden halte für ich keine gute Option.

    Bei so viel Ignoranz, Sturheit, Unwissenheit und dem nicht-vorhandensein von Lernfähigkeit hilft halt nichts mehr.

    Tuxist schrieb:

    Und es ist natürlich sicherer die C++ Funktionen zu verwenden aber das Debugging erleichtert es nicht unbedingt.

    Lern halt, deinen Debugger zu benutzen, du Wurst. Wenn du die C++-Klassen verwenden würdest, bräuchtest du ihn auch gar nicht so oft.

    Tuxist schrieb:

    Und eine Tatsache kann man nicht von tisch Wischen C++ ist ein um die Objektorientierung und paar Annehmlichkeiten erweitertes

    Falsch. C++ war das einmal, ist aber mittlerweile zu einem selbstständigen Lebewesen mutiert.

    Tuxist schrieb:

    Was besonders in den Compilern merken, dar läuft es meistens so ab C++ -> C -> Asm.

    Mit Sicherheit nicht.

    Tuxist schrieb:

    Nun kann man in bestimmten Situation besonders wenn es darum Leistung zu optimieren einen schritt weglassen.

    Aber nur, wenn es Sinn ergibt. Und das tut's in deinem Fall nicht.

    Tuxist schrieb:

    Wichtig ist es einfach auch zu wissen was grob im Background passiert.

    Und genau da liegt dein Problem - du hast keine Ahnung von nichts.



  • Kann irgendjemand den Thread dicht machen, habe dazu einfach lust mich hier weiter
    hier beleidigen zu lassen. Es ist eh Sinnlos.



  • 🤡



  • ohhhhh verkraftet da jemand die wahrheit nicht?



  • xdxd schrieb:

    ohhhhh verkraftet da jemand die wahrheit nicht?

    Wie viel namen will man sich noch geben ?



  • mein gott ist doch scheiß egal wie der code von tuxist aussieht, so lange er nur alleine damit arbeiten muss. ist doch alles nur hobby-bastlerei



  • Also, der Umgangston war jetzt wirklich nicht toll.

    Wenn er die Ansicht hat, das so zu machen, dann kann man ihn ja belehren und weitermachen lassen.

    Manche Fehler muss man selbst machen und erkennen, bevor man die Lösung ein Leben lang akzeptiert und lebt.



  • Weil automatische Speicherverwaltung so effektiv ist, verwendet ein Spiel was so ziemlich mit das Ressourcen fressende überhaupt ist, keine automatische Speicherverwaltung.

    [url]
    https://github.com/mpapierski/doom3.gpl/blob/master/neo/idlib/Heap.cpp
    [/url]

    Dazu möchte ich gerne eine Antwort. Und das John Carmack kein Idiot ist hat er ja schon bewiesen.



  • Tuxist schrieb:

    Weil automatische Speicherverwaltung so effektiv ist, verwendet ein Spiel was so ziemlich mit das Ressourcen fressende überhaupt ist, keine automatische Speicherverwaltung.

    [url]
    https://github.com/mpapierski/doom3.gpl/blob/master/neo/idlib/Heap.cpp
    [/url]

    Dazu möchte ich gerne eine Antwort. Und das John Carmack kein Idiot ist hat er ja schon bewiesen.

    Und wieder mal hast du nichts verstanden. Das ist ein Allokator, keine manuelle Speicherverwaltung.



  • Eine Allokatorklasse dient meines erachtens primär um Speicher zu verwalten ?



  • Richtig, aber das ist nicht mit manueller Speicherverwaltung gemeint. Mit manueller Speicherverwaltung ist die direkte, durchgängige Verwendung von new und vor allem delete im Code gemeint.



  • Da es noch keiner weiter kritisiert hat:

    PI, du darfst nicht direkt in einen String schreiben. Es ist der Implementierung freigestellt, wie der String seinen Storage verwaltet. Die libstdc++ zb benutzt Reference-Counting und Copy-On-Write um sinnlose Kopien zu vermeiden. Wenn du dann direktreinschreibst, dann zeigen möglicherweise auch andere Strings auf den selben Bullshit.

    Der Andere: Lies die Dokumentation von recv bevor du "hilfst", ist ja grauenhaft.



  • Ich hatte gerade etwas Zeit und habe deinen Server neu geschrieben. Da ich nicht weiß, wie du deine Daten parst, habe ich erstmal das Parsing weggelassen. Da du direkt auf die C-API's zugreifst, (weiß der Teufel wieso) mache ich das auch so. Der Code besteht aus 4 Dateien. (3 Header + main.cpp) Ich hoffe mal, du erkennst einen Unterschied 😉

    socket.hpp:

    #ifndef SOCKET_HPP
    #define SOCKET_HPP
    
    #include <stdexcept>
    #include <string>
    #include <cstdint>
    #include <cstddef>
    #include <vector>
    
    #include <sys/socket.h>
    #include <netinet/in.h>
    
    typedef int sockfd_type;
    
    inline void close_socket(sockfd_type sockfd)
    {
    	if(sockfd != -1)
    		close(sockfd);
    }
    
    struct socket_error : std::runtime_error
    {
    	socket_error(std::string const& what)
    		: runtime_error(what)
    	{}
    };
    
    class socket_data
    {
    	friend class acceptor;
    	friend class client_socket;
    
    	sockfd_type sockfd;
    };
    
    struct client_socket
    {
    	client_socket(socket_data data)
    		: sockfd(data.sockfd)
    	{}
    
    	client_socket(client_socket&& other)
    		: sockfd(other.sockfd)
    	{
    		other.sockfd = -1;
    	}
    
    	client_socket& operator = (client_socket&& other)
    	{
    		close_socket(sockfd);
    		sockfd = other.sockfd;
    		other.sockfd = -1;
    		return *this;
    	}
    
    	~client_socket()
    	{
    		close_socket(sockfd);
    	}
    
    	// Schlechte Methode, um zeilenweise zu lesen. Damit ich nicht alles mache, darfst du einen Buffer in die Klasse einbauen.
    	std::string read_line()
    	{
    		std::string result;
    
    		while(peek_two_chars() != "\r\n")
    			result += read_char();
    
    		skip_two_chars();
    
    		return result;
    	}
    
    	void write(std::string const& str)
    	{
    		char const* data = str.c_str();
    		std::size_t size = str.size();
    
    		int sendret;
    		while(size)
    		{
    			sendret = send(sockfd, data, size, 0);
    			data += sendret;
    			size -= sendret;
    		}
    
    		if(sendret == -1)
    			throw socket_error("disconnected");
    	}
    
    	client_socket(client_socket const&) = delete;
    	client_socket& operator = (client_socket const&) = delete;
    
    private:
    	std::string peek_two_chars() { return read_exactly(2, MSG_PEEK); }
    	void skip_two_chars() { read_exactly(2, 0); }
    	std::string read_char() { return read_exactly(1, 0); }
    
    	std::string read_exactly(std::size_t n, int flags)
    	{
    		std::vector<char> buf(n);
    
    		std::size_t recvret = recv(sockfd, buf.data(), n, flags);
    
    		switch(recvret)
    		{
    		case -1:
    			throw socket_error("recv() failed");
    
    		case 0:
    			throw socket_error("disconnected");
    
    		default:
    			if(recvret == n)
    				return std::string(buf.begin(), buf.end());
    
    			return std::string(buf.begin(), buf.end()) + read_exactly(n - recvret, flags);
    		}
    	}
    
    	sockfd_type sockfd;
    };
    
    struct acceptor
    {
    	acceptor(std::uint16_t port, std::size_t backlog)
    		: sockfd(socket(PF_INET, SOCK_STREAM, 0))
    	{
    		if(sockfd == -1)
    			throw socket_error("socket() failed");
    
    		sockaddr_in addr;
    		addr.sin_family = AF_INET;
    		addr.sin_addr.s_addr = INADDR_ANY;
    		addr.sin_port = htons(port);
    
    		if(bind(sockfd, reinterpret_cast<sockaddr const*>(&addr), sizeof addr) == -1)
    			throw socket_error("bind() failed");
    
    		if(listen(sockfd, static_cast<int>(backlog)) == -1)
    			throw socket_error("listen() failed");
    	}
    
    	acceptor(acceptor&& other)
    		: sockfd(other.sockfd)
    	{
    		other.sockfd = -1;
    	}
    
    	acceptor& operator = (acceptor&& other)
    	{
    		close_socket(sockfd);
    		sockfd = other.sockfd;
    		other.sockfd = -1;
    		return *this;
    	}
    
    	~acceptor()
    	{
    		close_socket(sockfd);
    	}
    
    	client_socket accept()
    	{
    		socket_data data;
    
    		if((data.sockfd = ::accept(sockfd, 0, 0)) == -1)
    			throw socket_error("accept() failed");
    
    		return client_socket(data);
    	}
    
    	acceptor(acceptor const&) = delete;
    	acceptor& operator = (acceptor const&) = delete;
    
    private:
    	sockfd_type sockfd;
    };
    
    #endif // SOCKET_HPP
    

    thread.hpp:

    #ifndef THREAD_HPP
    #define THREAD_HPP
    
    #include <functional>
    #include <utility>
    #include <pthread.h>
    
    struct thread
    {
    	thread(std::function<void()> f)
    		: functor(std::move(f))
    	{
    		pthread_create(&handle, 0, &thread::thread_func, this);
    	}
    
    	thread(thread&& other)
    		: handle(other.handle)
    		, functor(std::move(other.functor))
    	{
    		other.handle = pthread_t();
    	}
    
    	thread& operator = (thread&& other)
    	{
    		pthread_detach(handle);
    		handle = other.handle;
    		functor = std::move(other.functor);
    		other.handle = pthread_t();
    		return *this;
    	}
    
    	void join()
    	{
    		pthread_join(handle, 0);
    	}
    
    	~thread()
    	{
    		pthread_detach(handle);
    	}
    
    	static void* thread_func(void* this_)
    	{
    		static_cast<thread*>(this_)->functor();
    		return 0;
    	}
    
    	thread(thread const&) = delete;
    	thread& operator = (thread const&) = delete;
    
    private:
    	pthread_t handle;
    	std::function<void()> functor;
    };
    
    #endif // THREAD_HPP
    

    thread_group.hpp:

    #ifndef THREAD_GROUP_HPP
    #define THREAD_GROUP_HPP
    
    #include <functional>
    #include <utility>
    #include <vector>
    
    #include "thread.hpp"
    
    struct thread_group
    {
    	void create_thread(std::function<void()> f)
    	{
    		threads.emplace_back(std::move(f));
    	}
    
    	~thread_group()
    	{
    		for(thread& t : threads)
    			t.join();
    	}
    
    private:
    	std::vector<thread> threads;
    };
    
    #endif // THREAD_GROUP_HPP
    

    main.cpp:

    #include <iostream>
    #include <vector>
    #include <memory>
    #include <cstdlib>
    
    #include "socket.hpp"
    #include "thread_group.hpp"
    
    // Der shared_ptr ist ein Workaround für einen Bug in der GCC-Standardlib. Ich würde hier lieber direkt einen client_socket& nehmen.
    void handle_client(std::shared_ptr<client_socket>& socket_ptr)
    {
    	try
    	{
    		// Die Ausgaben sind hier nicht synchronisiert, das sollte noch gemacht werden.
    		for(std::string line; (line = socket_ptr->read_line()) != "";)
    			std::cout << line << '\n';
    
    		std::cout << std::endl;
    
    		// Hier ist Platz für deinen Code.
    	}
    
    	catch(socket_error const& e)
    	{
    		std::cerr << e.what() << '\n';
    	}
    }
    
    int main(int argc, char** argv)
    {
    	int const default_backlog = 42;
    
    	if(argc < 2 || argc > 3)
    	{
    		std::cerr << "usage: mysuperhttpserver <port> [backlog=" << default_backlog << ']';
    		return 1;
    	}
    
    	int const port = std::atoi(argv[1]);
    	int const backlog = argc == 3 ? std::atoi(argv[2]) : default_backlog;
    
    	thread_group threads;
    
    	try
    	{
    		acceptor acc(port, backlog);
    
    		for(;;)
    		{
    			std::shared_ptr<client_socket> socket = std::make_shared<client_socket>(acc.accept());
    			threads.create_thread(std::bind(&handle_client, std::move(socket)));
    		}
    	}
    
    	catch(socket_error const& e)
    	{
    		std::cerr << e.what() << '\n';
    		return 2;
    	}
    }
    

    Falls irgendwas unklar sein sollte, einfach fragen. Ich hoffe, du kopierst den Code nicht einfach 🤡



  • std::vector<char> buf(n);
    

    Verstehe nicht warum ich Daten von denen ich noch nicht weiß ob sie zusammenhängend sind eine nummerierte liste schreiben soll ? Es werden immer 255 zeichen geholt aber es könnte auch 1/4 oder 1/6 ... whatever von einem Dokument sein. Die Daten können auch unvollständig sein. Ich verstehe nicht wie mir dar eine Liste hilfreich sein kann ? Das Problem ist das http die Verbindung offen hält und deswegen das Parsen Asyncron von httpd laufen muss.



  • std::vector<char>(n) ist ähnlich nutzbar wie new char[n], nur deutlich nützlicher vom Umfang und leichter zu verwenden.

    Du brauchst einfach einen Eingangsbuffer, der recv emntgegennimmt. Was du dann damit machst ist dir überlassen.



  • jup dieser Eigangsbuffer muss anhand des http Startheader in aufgeteilt werden, hatte hierbei an memmmove gedacht um die Daten weiter zu verarbeiten was man z.b. in einem Vector oder Array machen kann. Die Kompletten http Daten könnte man dann mit strsep in Kopf und Daten aufteilen und dann einen cgiparser laufen lassen.



  • Ethon schrieb:

    Der Andere: Lies die Dokumentation von recv bevor du "hilfst", ist ja grauenhaft.

    Ich vermute, du sprichst mich damit an.
    Auf welchen Beitrag beziehst du dich?



  • Tuxist schrieb:

    Das Problem ist das http die Verbindung offen hält und deswegen das Parsen Asyncron von httpd laufen muss.

    Ist doch gar kein Problem. Für jeden Client wird ein eigener Thread erstellt, in dem dieser dann bearbeitet wird. Eine professionellere Lösung hätte vermutlich Worker-Threads verwendet, da hier pro Client ein Thread quasi-geleakt wird. (Die Threads werden erst bei Programmende aufgeräumt.)

    Tuxist schrieb:

    jup dieser Eigangsbuffer muss anhand des http Startheader in aufgeteilt werden, hatte hierbei an memmmove gedacht um die Daten weiter zu verarbeiten was man z.b. in einem Vector oder Array machen kann. Die Kompletten http Daten könnte man dann mit strsep in Kopf und Daten aufteilen und dann einen cgiparser laufen lassen.

    Pfui pfui pfui!
    Du kriegst die Daten vom Socket doch schon zeilenweise als std::string's. Du brauchst also nur noch diese Zeilen zu verarbeiten und dem Client eine Antwort zu senden.


Anmelden zum Antworten