Mehrere TCP/IP Verbindungen



  • mehr schrieb:

    TL;DR: Keiner von euch hat Erfahrung und ihr ratet alle rum.

    Machen das nicht alle Softwareentwickler die ganze Zeit? Erfahrung fällt nicht vom Himmel.



  • mehr schrieb:

    TL;DR: Keiner von euch hat Erfahrung und ihr ratet alle rum.

    Bei welchem Punkt?

    Aber das alle rumraten, das glaube ich nicht.



  • Jodocus schrieb:

    Die sollen dann alle ihre eigenen Kernel-Mode-Treiber schreiben (á la Microsoft IIS' HTTP-Treiber) und lock-freie Algorithmen benutzen: allesamt extrem hoch spezialisierte Einzelbereiche. Kaum auszudenken, wie einem da die Bugs, ABA-Probleme, Deadlocks (und dank Treiber auch noch) Kernel-panics/Bluescreens um die Ohren fliegen werden.

    Oder sie nutzen halt Projekte wie PF_RING. 🙄



  • Oder die 99.99% der Entwickler die Software schreiben wo es vollkommen egal ist halten einfach den Rand, anstatt dass sie versuchen ihre Kollegen zu missionieren.

    Ich möchte nicht wissen wie viel Geld vernichtet wurde durch schlechte Ratschläge wie "mach mal asynchron weil sonst is voll langsam". Und dann sitzt Otto-Normalprogrammierer da und programmiert sich nen Ast ab für den < 1 call/second Server. Was sonst in 10 Minuten erledigt gewesen wäre.



  • Wären 2 Threads pro Client nicht angebrachter, da TCP Sockets full duplex sind?



  • hustbaer schrieb:

    Mein Vorschlag wäre pro Verbindung einen Thread zu nehmen, und dann alles mit synchronem IO zu machen.
    Super einfach und wer behaupten möchte dass es von der Performance her schlecht ist, der soll es mir erstmal beweisen 😉

    Danke, da ich bisher wenig mit Threads gearbeitet habe. Kannst du es mir bitte etwas genauer erläutern?

    Gruß



  • socket = socket()
    bind(socket)
    listen(socket)
    while(true) {
       new_socket = accept(socket)
       new_thread(new_socket);
    }
    
    void new_thread(socket) {
       recv(socket);
       send(socket);
       ...
       closesocket(socket);
    }
    


  • Danke für die Antwort.

    Achso logich der Thread bleibt beim ersten resv hängen aber dafür wird dann der nachfolgende Thread ausgeführt.

    kapiert!



  • ^ Oder ?

    Also wenn anschließend ein zweiter Thread kommen würde. Würde dann der erste bei resv einen blocking call erhalten und der nächste würde ausgeführt werden?

    Danke Gruß



  • Der Hauptthread empfängt neue Clients und deligiert sie in neue Threads, die dann blocken/whatever machen. Er selber blockiert nur während er auf neue Clients wartet, sonst nicht. Falls doch, dann würden halt Clients nicht mehr akzeptiert werden und früher oder später abgewiesen.



  • hustbaer schrieb:

    Ich möchte nicht wissen wie viel Geld vernichtet wurde durch schlechte Ratschläge wie "mach mal asynchron weil sonst is voll langsam". Und dann sitzt Otto-Normalprogrammierer da und programmiert sich nen Ast ab für den < 1 call/second Server. Was sonst in 10 Minuten erledigt gewesen wäre.

    Ach, aber "Otto" kommt von selbst auf die Idee einen neuen Thread für die Verbindung zu erstellen?
    "Otto" hat typischerweise auch keine Ahnung von Multi-Threading und produziert fröhlich Abstürze. Ich möchte nicht wissen, wie viel Geld seiner Kunden dadurch vernichtet wurde.
    Ich halte diese Otto-Normalidiotenbeispiele für schädlicher als die guten Ratschläge. Die zementieren nur die Don't-Care-Mentalität, die ohnehin schon zu präsent ist. Wenn man von seinen Mitmenschen erwartet, dass sie sich idiotisch verhalten, werden so das auch tun.
    Sinnvoller fände ich es den Otto mal grundsätzlich über Nebenläufigkeit aufzuklären. Wenn er verstanden hat, was alles möglich ist, kann er gerne einen Thread pro Client verwenden. Wenn man ihm zusäzlich erklärt hat, wie man Software-Komponenten voneinander trennt, kann er später problemlos seinen Thread-Ansatz überarbeiten, falls es mal erforderlich werden sollte.



  • Hier mal ein kleiner Echo-Server (für Linux), der pro Verbindung einen neuen Thread aufmacht:

    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    
    #include <iostream>
    #include <thread>
    #include <vector>
    
    int main() {
        using namespace std;
        int listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if(listen_socket < 0) {
            cerr << "socket() failure\n";
            return -1;
        }
    
        sockaddr_in saddr{ };
        saddr.sin_port = htons(40001);
        saddr.sin_family = AF_INET;
        saddr.sin_addr.s_addr = htonl(INADDR_ANY);
    
        if(bind(listen_socket, reinterpret_cast<sockaddr*>(&saddr), sizeof saddr) < 0) {
            cerr << "bind() failure\n";
            close(listen_socket);
            return -2;
        }
    
        if(listen(listen_socket, SOMAXCONN) < 0) {
            cerr << "listen() failure\n";
            close(listen_socket);
            return -3;
        }
    
        for(;;) {
            sockaddr_in client_addr{ };
            socklen_t addr_len = sizeof client_addr;
            int client_socket = accept(listen_socket, reinterpret_cast<sockaddr*>(&client_addr), &addr_len);
            if(client_socket < 1) {
                cerr << "accept() failure\n";
            }
            else { 
                thread([=]() {
                    vector<char> buffer(512);
                    int result = 0;
                    do {
                       result = recv(client_socket, &buffer[0], buffer.size(), 0);
                       if(result < 0) {
                          cerr << "recv() failure\n";
                          close(client_socket);
                          return;
                       }
                       else if(result != 0) {
                           if(send(client_socket, &buffer[0], result, 0) < 0) {
                              cerr << "send() failure\n";
                              close(client_socket);
                              return;
                           }
                       }
                       else {
                           close(client_socket);
                           return;
                       }
                   } while(true);
                }).detach();
            }
        }
    }
    

    Edit: Trotzdem sei dir schwer geraten, eine Library zu benutzen. Dann musst du dich auch nicht mit Plattformabhängigkeit und I/O-Strategien wie 1 Thread pro Verbindung etc. rumplagen.



  • TyRoXx schrieb:

    Ach, aber "Otto" kommt von selbst auf die Idee einen neuen Thread für die Verbindung zu erstellen?

    Jupp, klar.



  • Jodocus schrieb:

    Edit: Trotzdem sei dir schwer geraten, eine Library zu benutzen. Dann musst du dich auch nicht mit Plattformabhängigkeit und I/O-Strategien wie 1 Thread pro Verbindung etc. rumplagen.

    Jop.
    Boost.Asio ist z.B. nicht grundsätzlich böse. Man muss nur "As" Teil ignorieren 🕶



  • Jodocus schrieb:

    Hier mal ein kleiner Echo-Server (für Linux), der pro Verbindung einen neuen Thread aufmacht:

    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    
    #include <iostream>
    #include <thread>
    #include <vector>
    
    int main() {
        using namespace std;
        int listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if(listen_socket < 0) {
            cerr << "socket() failure\n";
            return -1;
        }
    
        sockaddr_in saddr{ };
        saddr.sin_port = htons(40001);
        saddr.sin_family = AF_INET;
        saddr.sin_addr.s_addr = htonl(INADDR_ANY);
    
        if(bind(listen_socket, reinterpret_cast<sockaddr*>(&saddr), sizeof saddr) < 0) {
            cerr << "bind() failure\n";
            close(listen_socket);
            return -2;
        }
        
        if(listen(listen_socket, SOMAXCONN) < 0) {
            cerr << "listen() failure\n";
            close(listen_socket);
            return -3;
        }
    
        for(;;) {
            sockaddr_in client_addr{ };
            socklen_t addr_len = sizeof client_addr;
            int client_socket = accept(listen_socket, reinterpret_cast<sockaddr*>(&client_addr), &addr_len);
            if(client_socket < 1) {
                cerr << "accept() failure\n";
            }
            else { 
                thread([=]() {
                    vector<char> buffer(512);
                    int result = 0;
                    do {
                       result = recv(client_socket, &buffer[0], buffer.size(), 0);
                       if(result < 0) {
                          cerr << "recv() failure\n";
                          close(client_socket);
                          return;
                       }
                       else if(result != 0) {
                           if(send(client_socket, &buffer[0], result, 0) < 0) {
                              cerr << "send() failure\n";
                              close(client_socket);
                              return;
                           }
                       }
                       else {
                           close(client_socket);
                           return;
                       }
                   } while(true);
                }).detach();
            }
        }
    }
    

    Edit: Trotzdem sei dir schwer geraten, eine Library zu benutzen. Dann musst du dich auch nicht mit Plattformabhängigkeit und I/O-Strategien wie 1 Thread pro Verbindung etc. rumplagen.

    Danke für die Antwort und das Beispiel. Aber ist es hier nicht so das alle Verbindungen über den gleichen Port laufen?

    Gruß



  • @volkard

    Beachte, daß http-serven trivial ist. Apache ist im Prinzip ein single-Threaded Nacheinanderausführer.

    aber auch nicht mehr wenn du 1000 Benutzer auf deiner Seite hast - irgendwann sieht das ganze - wenn man die Sockets betrachtet auch nicht mehr anders aus als ein stark frequentierter Chat-Server

    @hustbear

    heisse luft
    wo sind deine zahlen?
    wo sind die sources mit denen man die benchmarks selbst wiederholen kann?

    sind leider nur Erfahrungswerte aus meiner berufliche Praxis - habe dazu gerade keine "Studie" vorliegen, wenn du andere Erfahrungen gemacht hast würde ich mich gerne von deinen Lösungen hören - teilweise muss meine Software auch auf kleinen embedded Systemen laufen, deswegen achte ich sehr stark auf Skalierung oder auch die "Belastung" welche ganz klar von System zu System anders gelagert ist

    100 µs sind schon einmal die richtige Größenordnung. Invertiert sind das gerade einmal 10 kHz. Maximal gehen damit also 10000 Verbindungen pro Sekunde und Hardware-Thread.

    nicht zu vergessen wieviel Zeit dein Scheduler deinem Prozess überhaupt zuspricht und deine Threads sich um die Sub-Zuteilung schlagen (was mehr ist als die pure Switch-Zeit), plus die Contet-Switch-Cache-Misses und,und,und

    für seinen Chat-Server hier reicht aber wie gesagt sicherlich die Thread-Lösung



  • Brause schrieb:

    Danke für die Antwort und das Beispiel. Aber ist es hier nicht so das alle Verbindungen über den gleichen Port laufen?

    Jo, das wäre normal. Auf Port 40001 bietet dieser Server seine Dienste an.
    Die Verbindungen (bestehend aus je zwei Ports und zwei Adressen) sind dann unabhängig.



  • volkard schrieb:

    Brause schrieb:

    Danke für die Antwort und das Beispiel. Aber ist es hier nicht so das alle Verbindungen über den gleichen Port laufen?

    Jo, das wäre normal. Auf Port 40001 bietet dieser Server seine Dienste an.
    Die Verbindungen (bestehend aus je zwei Ports und zwei Adressen) sind dann unabhängig.

    Danke für die Antwort! War keine schlaue frage klar gibt ja zweit Sockets auf Server Seite!! Hatte einen denkfehler.

    Bei mir sieht es nämlich etwas anders aus. Da die angesprochenen Verbindungen Maschinen sind die komplett autonom sind ist meine Idee das die Maschinen jeweils der Server sind und ich der Client. Also das mein Programm jeweils einen Client für jede Maschine zur Verfügung stellt. Wie würde man das am besten mit Threads realisieren?

    Danke Gruß



  • Gast3 schrieb:

    @hustbear

    heisse luft
    wo sind deine zahlen?
    wo sind die sources mit denen man die benchmarks selbst wiederholen kann?

    sind leider nur Erfahrungswerte aus meiner berufliche Praxis - habe dazu gerade keine "Studie" vorliegen, wenn du andere Erfahrungen gemacht hast würde ich mich gerne von deinen Lösungen hören

    Ja, ich hab die Erfahrung gemacht dass es so-gut-wie egal ist ob man jetzt 10 oder 1000 Threads hat - so lange nicht mehr gleichzeitig laufen wollen als man physikalische CPUs hat merkt man keinen Unterschied.

    Vielleicht hast du noch mit einem Kernel aus Grossvaters Zeiten gearbeitet wo das Suchen eines aufzuweckenden Threads noch O(N) war?



  • Ja, ich hab die Erfahrung gemacht dass es so-gut-wie egal ist ob man jetzt 10 oder 1000 Threads hat - so lange nicht mehr gleichzeitig laufen wollen als man physikalische CPUs hat merkt man keinen Unterschied.

    und was ist wenn die gleichzeitigen deine physikalische CPU-Anzahl (du meinst Hardware-Threads?) per se überschreiten?
    denn erst da wird ja die Skalierbarkeit der Software überhaupt wichtig
    und eher das Szenario das ich beschrieben habe

    dieses "so lange nicht mehr gleichzeitig laufen wollen" ist genau das Skalierungsproblem an dem Thread-per-Client Prinzip - entweder beschränkt man die Client-Anzahl und/oder man bremst die "gleichzeitige" Kommunikation der Teilnehmer - beides könnte irgendwie den Sinn eines Chat-Server beschränken
    - und ist auch stark davon abhängig wieviel Daten über den Server zwischen den Clients ausgetauscht werden, weil dann ja auch der Synchronisationsaufwand steigt

    wenn die Client-Anzahl - also die parallel zu behandelnden Aufgaben gering ist oder die Durchsatzminderung bei
    steigender Clientanzahl irgendwie egal (unbewertet) ist - und auch die Systemressourcen da mitspielen kann man Threads leicht und einfach benutzen

    eines meiner Projekte: eine Win7/Linux 3.x,x64-Software welche mit ~230 embedded-Steuerungen über TCP-IP kommuniziert, lesend/schreibend - ~3-50ms, 100-500Byte Pakete, permanent, (fast) keine Allokationen aber Synchronisation zwischen Client-Daten - die Software ist da so eine Art Chat-Server-Message-Router - Linux macht es besser (aber Kunde mag leider Windows)

    es gab aber auch schon die Frage ob man nicht auch 3000 solcher embedded Systeme über so einen Server kommunizieren zu lassen - da musst dann aber wirklich mehr CPU,RAM,Netzwerke und Linux rein

    du hast wenigstens deine Einschränkung mit dem "so lange nicht mehr gleichzeitig laufen wollen" gebracht - manch andere Post hier könnte man teilweise so verstehen als wenn man immer alles mit Threads machen könnte

    Vielleicht hast du noch mit einem Kernel aus Grossvaters Zeiten gearbeitet wo das Suchen eines aufzuweckenden Threads noch O(N) war?

    naja so alt bin ich auch wieder nicht - habe aber noch Väterchen-Win2K-Software die Flausen ausgetrieben mit Threads skalieren zu wollen - und auch noch mit 16Bit-Interrupts "Threading" betrieben 🙂

    aber ich arbeite auch in einem Bereich in dem die Systemressoucen eines Win/Linux Industrie-PCs oder embedded-System wie RAM,CPU usw. nicht wie Kamelle
    vom Wagen geworfen werden wenn die Software schreit


Anmelden zum Antworten