select() für dumme



  • Kann mir eine in einfachen worten die einzelnen parameter von select erklären und wie das eingesetzt wird, also was das mit diesem fd_set aufsich hat und so. und vielleich noch ein (nich allzu großen) beispiel code.
    Ich hab mir bestimmt schon 5 unterschiedliche beschreibungen angesehen und nichts davon kapiert(doch: der letzt parameter ist für timeout zuständig)



  • Das muss nach Linux, select() gibts in Standard C++ nicht.

    Der erste Parameter entspricht dem Wert des Höchsten Descriptors (Handles) + 1. Der zweite Parameter gibt ein Feld mit Handeln an, die zum Lesen zuständig sind. Der dritte ein Feld mit Handeln zum Schreiben, vierte für Fehlermeldungen, fünfter für Timeout.

    Angenommen, du definierst STDIN als 0 und hast ein Feld fd_set lesen. Du möchtest gerne benachrichtigt werden, wenn von STDIN gelesen werden kann. Dann musst du zuerst das Feld lesen in einen Zustand versetzen, in dem es Descriptors aufnehmen kann. Das machst du mit dem Makro FD_ZERO():

    FD_ZERO(&lesen);

    Dann fügst du STDIN zu lesen hinzu:
    FDSET(STDIN, &lesen);

    Nun kannst du schon select() aufrufen:

    select(STDIN + 1, lesen, NULL, NULL, NULL);
    if (FD_ISSET(STDIN, &lesen))
     printf("es wurde von stdin gelesen!\n");
    

    Du musst im Hinterkopf behalten, daß select() die Felder verändert. D.h., wird von STDIN nicht gelesen, sondern von einem anderen Handle, das jetzt nicht weiter von Interesse ist, wird STDIN aus dem Feld (fd_set) entfernt und der Ausdruck FD_ISSET(STDIN, &lesen) ist 0.
    select() wird häufig bei Serverapplikationen angewendet die nicht auf fork() basieren, um Clientanfragen parallel zu bearbeiten. Die Client-Sockets sind dann alle in einem fd_set drin und so kann immer schön abgefragt werden, wo und ob gerade Daten kommen.



  • Original erstellt von Doktor Prokt:
    **Das muss nach Linux, select() gibts in Standard C++ nicht.
    **

    jup
    Thread verschoben ins Unix Forum.



  • Warum klappt das nich:

    anfang des codes:

    #include <winsock.h>
    #include <stdio.h>
    #include <conio.h>
    
    #define MAXCLIENTS 10
    #define BUFFERSIZE 1024
    
    struct user{
        char name[50];
        int socket;
        user *prev;
        user *next;
    };
    
    user server;
    
    int user_nr=0;
    char buffer[BUFFERSIZE];
    

    und die funktion mit select:

    int interchange(void)
    {
        fd_set fds;
        FD_ZERO(&fds);
    
        struct timeval tv;
        tv.tv_sec=5;
        tv.tv_usec=0;
    
        user *p=&server;
        while(p->next!=NULL);
        {
            p=p->next;
            FD_SET(p->socket,&fds);
            select(MAXCLIENTS+1,&fds,NULL,NULL,&tv);
            recv(p->socket,buffer,sizeof(BUFFERSIZE)-1,0);
            if(FD_ISSET(p->socket,&fds))
            {
                printf("\ngelesen\n");
            }else printf("\n timeout\n");
        }
    return 0;
    }
    

    Er bleibt immer beim recv hängen.
    PS. lasst euch von dem ganzen drum herrum nich verwirren.

    [ Dieser Beitrag wurde am 03.11.2002 um 20:14 Uhr von TheDeath editiert. ]



  • Ist doch klar, daß er hängen bleibt:

    recv() wartet so lange, bis es was received.
    recv() darf erst NACH der Abfrage if(FD_ISSET(...)) aufgerufen werden. Dann funktioniert es. Es muss also im if-Block stehen.



  • So? geht immer noch nich, bleibt nach dem aufruf der schleife hänge, also er gibt auf dem monitor "schleife beginnt" aus und macht dann nix mehr.

    int interchange(void)
    {
        fd_set fds;
        FD_ZERO(&fds);
    
        struct timeval tv;
        tv.tv_sec=5;
        tv.tv_usec=0;
    
        user *p=&server;
    
        printf("schleife beginnt\n");
    
        while(p->next!=NULL);
        {
            p=p->next;
            FD_SET(p->socket,&fds);
    
            select(MAXCLIENTS+1,&fds,NULL,NULL,&tv);
    
            printf("select ok");
    
            if(FD_ISSET(p->socket,&fds))
            {
                recv(p->socket,buffer,sizeof(BUFFERSIZE)-1,0);
    
                printf("\ngelesen\n");
            }else printf("\n timeout\n");
        }
    return 0;
    }
    


  • Hm, jo, select() darfst du natürlich nur einmal aufrufen.

    Versuchs mal so:

    int interchange(void)
    {
        int i;
        fd_set fds;
        FD_ZERO(&fds);
    
        struct timeval tv;
        tv.tv_sec=5;
        tv.tv_usec=0;
    
        user *p=&server;
    
        printf("schleife beginnt\n");
    
        while(p->next!=NULL);
        {
            p=p->next;
            FD_SET(p->socket,&fds);
        }
    
            select(MAXCLIENTS+1,&fds,NULL,NULL,&tv);
    
            printf("select ok");
         for (i = 0; i <= MAXCLIENTS; i++)
         {
    
            if(FD_ISSET(i,&fds))
            {
                recv(i,buffer,sizeof(BUFFERSIZE)-1,0);
    
                printf("\ngelesen\n");
            }
         }
    return 0;
    }
    

    So sollte es funktionieren.



  • klappt immer noch nich, der mag das select nich, die schleife startet er ganz normal nur kommt dann KEINE meldung das select ausgeführt wurde.
    achja, das programm soll für windows sein, kann es sein das die funktion da nich funktioniert?



  • Hm, keine Ahnung, wusste bis gestern gar nicht, daß es select() auch für Windows gibt. Auf jeden Fall sollte er "select ok" spätestens nach deiner vereinbarten TimeOut-Zeit ausführen. Das einzige, was ich mir noch vorstellen kann, ist, daß du vor dem Aufruf der Funktion interchange() eine andere Funktion aufrufst, die das Programm blockiert und auf irgendwas wartet. Oder es kann sein, daß die Liste falsch implmentiert ist und er keinen Pointer mit dem Wert NULL findet...
    Poste einfach mal mehr Code, dann finden mer das schon raus.



  • **
    achja, das programm soll für windows sein, kann es sein das die funktion da nich funktioniert?
    **
    Vieles funktioniert unter Windows nicht... aber "select" sollte schon gehen 😃

    Was für ein Return-Wert kriegst du eigentlich von select?
    Ohne errno oder return-Wert weist du nicht ob das geklappt hat...



  • Ich pack das mal in das WinAPI Forum, wenn es um Windos geht, da die WinAPI Socket Unterstützung doch sehr simpel und primitiv ist im Gegensatz zu den POSIX Sockets und du sicher mit anderen Problemen kämpfen musst.

    hier sind ein paar Links, die dir sicher helfen (sind aber für Unix)

    http://www.acme.com/software/thttpd/ <-- Beispiel Programm

    @Doktor
    select muss man doch eigentlich in die Schleife packen! Wie soll select sonst die Bitmaske setzen?

    [ Dieser Beitrag wurde am 04.11.2002 um 20:31 Uhr von kingruedi editiert. ]



  • Original erstellt von kingruedi:
    **

    @Doktor
    select muss man doch eigentlich in die Schleife packen! Wie soll select sonst die Bitmaske setzen?
    **

    Nein, select() für jedes Socket aufzurufen, wäre unsinnig. Die stehen doch alle im fd_set, das an select() übergeben wird.

    Falls du aber bei Servern die Hauptschleife meinst, die immer durchlaufen wird, steht select() natürlich schon in einer Schleife 🙂



  • das MAXCLIENTS kannste nicht verwenden. koennte schliesslich sein, dass die zugewiesene socketdeskriptoren einen groesseren wert haben. musst das maximum aller sockets bestimmen. auch die for(i=0;i<=MAXCLIENTS;++i){} schleife ist demnach falsch, da z.b. i=0 normalerweise stdin ist. hat die linked list auch gueltige sockets? ansonsten scheints eigentlich richtig zu sein, aber von win hab ich keine ahnung, vielleicht muss man da mehr machen.



  • hier is mal mein completter code, ich hab festgestellt, das der fehler irgendwo in der ersten schleife bei interchange() liegt, weis aber nich warum.

    #include <winsock.h>
    #include <stdio.h>
    #include <conio.h>
    
    #define MAXCLIENTS 10
    #define BUFFERSIZE 1024
    
    struct user{
        char name[50];
        int socket;
        user *prev;
        user *next;
    };
    
    int adduser(char name[50],int socket);
    int interchange(void);
    user* prev(void);
    int ausgabe(void);
    int loeschen(void);
    
    user server;
    
    int user_nr=0,max_sock=0;
    char buffer[BUFFERSIZE];
    
    int main(int argc, char *argv[])
    {
        server.next=NULL;
        server.prev=NULL;
    
        char name[50];
        int cli_size, bytes,s,c,retval;
    
        WSADATA wsa;
    
        fd_set fds;
    
        struct sockaddr_in cli;
    
        struct sockaddr_in serv;
        serv.sin_addr.s_addr=INADDR_ANY;
        serv.sin_port=htons(80);
        serv.sin_family=AF_INET;
    
        char willkomm[]="Willkommenn auf dem Chat-Server\r\n";
    
        FILE *log;
        log=fopen("log.txt","w");
        fprintf(log,"This file was build by chat_srv\n\n");
    
    //WSA initialisieren
        if(WSAStartup(MAKEWORD(1,1),&wsa))
        {
            fprintf(log,"WSAStartup() failed %lu\n",(unsigned long)GetLastError());
            return EXIT_FAILURE;
        }else fprintf(log, "WSAStartup() successful started\n");
    //###################
    
    //socket erstellen
        if((s=socket(AF_INET,SOCK_STREAM,0))==-1)
        {
            fprintf(log,"Error, creating socket failed\n");
        }else fprintf(log,"Socket successful created\n");
    //###################
    
    //port an locale ip binden
        if(bind(s,(struct sockaddr *)&serv,sizeof(serv)) ==-1)
        {
            fprintf(log,"Couldn't bind port %i to lokalhost\n",s);
        }else fprintf(log,"Port %i successful bound to localhost\n",s);
    //##################
    
    //socket für verbindungen öffnen
        if(listen(s,4)==-1)
        {
            fprintf(log,"Couldn't open socket for Client connections");
        }else fprintf(log,"Port successful for Client-Connections opened");
    //##################
    
        cli_size=sizeof(cli);
    
        for(;;)
        {
    
            FD_ZERO(&fds);
            FD_SET(s,&fds);
    
            if(user_nr<MAXCLIENTS)
            {
                if(FD_ISSET(s,&fds))
                {
                    if((c=accept(s,(struct sockaddr*)&cli,&cli_size))==-1)
                    {
                        printf("Error, accept");
                        return -1;
                    }
                    if((bytes=recv(c,name,50,0))==-1)
                    {
                        printf("Error, name");
                        return -1;
                    }
                    name[bytes]='\0';
                    adduser(name,c);
    
                    printf("neuer user: %s\nEs sind jetzt %i User auf dem Server",name, user_nr);
                    interchange();
                    printf("\ninterchange ende\n");
                }
    
            }
    
        }
    
    }
    
    int adduser(char *name, int socket)
    {
        int bytes;
        user_nr++;
    // zeiger auf neuen user namens client definieren
        user *client=new user;
    //z auf den letzten user setzen
        user *z=prev();
    
    //neue strukur füllen
        strcpy(client->name,name);
        client->socket=socket;
        client->next=NULL;
    
    //der nachvolger von letzten user wird auf den neuen gesetzt
        z->next=client;
    
    // der vorgänger der neuen wird auf den zeiger der letzten gesetzt
        client->prev=z;
        char user[100];
        sprintf(user,"%i",user_nr);
        if((bytes=send(socket,user,sizeof(user),0))==-1)
        {
            printf("slkjf");
        }
        printf("\ngesendet\n");
        return 0;
    }
    
    user *prev(void)
    {
        user *p=&server;
    
        while(p->next!=NULL)
        {
            if(p->socket>max_sock)
                max_sock=p->socket;
            p=p->next;
        }
        return p;
    }
    
    int interchange(void)
    {
        int i;
        fd_set fds;
        FD_ZERO(&fds);
    
        struct timeval tv;
        tv.tv_sec=5;
        tv.tv_usec=0;
    
        user *p=&server;
    
        printf("\nschleife beginnt\n");
    
        while(p->next!=NULL)
        {
            p=p->next;
            FD_SET(p->socket,&fds);
        }
        printf("schleife ende\n");
    
            select(max_sock+1,&fds,NULL,NULL,&tv);
    
            printf("select ok");
         for (i = 0; i <= MAXCLIENTS; i++)
         {
    
            if(FD_ISSET(i,&fds))
            {
                recv(i,buffer,sizeof(BUFFERSIZE)-1,0);
    
                printf("\ngelesen\n");
            }else printf("\n timeout \n");
         }
    return 0;
    }
    


  • kann mir denn keiner helfen?



  • du rufst prev() auf, wenn die neue verbindung noch nicht in der linked list verkettet ist, demnach wird diese verbindung bei der berechnung von max_sock auch nicht beruecksichtigt.
    wie das unter win ist weiss ich nicht, aber unter unix sollte man ausserdem den rueckgabewert von select() ueberpruefen und im falle von -1 die externe variable errno mit EINTR vergleichen.
    auch solltest du ueberpruefen, ob der log pointer, der auf eine FILE struktur zeigt, gueltig ist.


Anmelden zum Antworten