Problem mit UDP Multi-Client Server



  • Hallo an alle,

    ich möchte gerne einen Spieleserver zu einem vorhandenen Spiel schreiben.
    Dieser muss von verschiedenen Spieler Pakete empfangen, auswerten und manche von diesen verändert an alle anderen zurückschicken.
    Das Problem ist, dass das Protokoll UDP ist, und ich leider nur Multi-Client Tutorials für TCP gefunden hab.
    Folgendes hab ich schon gemacht: Der Server kann bereits Daten empfangen. Problem: Er weiß nicht von welchem Client das Paket kommt.
    Ich habe es versucht mit einem SOCKADDR_IN-Array zu lösen, was jedoch nur eine Zwischenlösung sein sollte, da nicht jedes Paket von einem Spieler gesendet werden kann, sondern auch umn z.B. den Servernamen abzufragen.
    Wie man sieht habe ich auch schon Kommentare eingebaut, die jedoch eher für mich sind. Nachher soll die Verbindung erst wirklich stehen, wenn eine Join Routine abgeklappert wurde.
    Dementsprechend bräuchte ich glaube ich zwei Bereiche: Einen für Spielerpakete und einen für die Restlichen. Aber wie kann ich sowas realisieren?
    Das Programm funktioniert auch im Moment nicht richtig, denn es zeigt unendlich mal "Nachricht von ID 32: " an, hätte ich nicht das break eingebaut.
    Ich hoffe ihr könnt mir einen Tipp geben. Warteschlangen für ein- und ausgehende Pakete wollte ich erst zum Schluss einbauen.

    Vielen Dank im Voraus,

    Gruß Jermuk

    /* Includes */
    #include <stdlib.h>
    #include <stdio.h>
    #include <winsock.h>
    
    /* Einstellungen */
    #define MAX_CLIENTS 32
    #define SERVER_PORT 36963
    
    int startWinsock(void);
    void error_exit(char *error_message);
    
    int main(void)
    {
    
        struct PEER
        {
            u_short	port;
            struct in_addr ip;
            unsigned short id;
            int used;
        };
    
        SOCKADDR_IN serverinfo; //Server und Client Informationen (IP, etc.)
        SOCKADDR_IN newclient;
        struct PEER peer[MAX_CLIENTS];
        int lesesocket; //Socket Deskriptoren
        int rtn; //Rückgabewert (Fehlerüberprüfung)
        int ready; // Sind noch Deskriptoren zu lesen
    
        int i; //Schleifenzähler
        int bekannt = -1;
    
        fd_set gesamt_sock; //???
    
        char buffer[1024]; // Buffer Initialisieren
    
        // Port etc. der Struktur hinzufügen
        serverinfo.sin_family = AF_INET;
        serverinfo.sin_addr.s_addr = INADDR_ANY;
        serverinfo.sin_port = htons(SERVER_PORT);
    
        // Winsock starten
        rtn = startWinsock();
        if(rtn != 0) error_exit("Winsock Initialisierung fehlgeschlagen! Fehlercode: ");
    
        // Socket erstellen
        lesesocket = socket(AF_INET, SOCK_DGRAM, 0);
        if(lesesocket==INVALID_SOCKET) error_exit("Socket Erstellung fehlgeschlagen! Fehlercode: ");
    
        // Socket bind()
        rtn = bind(lesesocket, (struct sockaddr *)&serverinfo, sizeof(serverinfo));
        if(rtn==SOCKET_ERROR) error_exit("bind() fehlgeschlagen! Fehlercode: ");
    
        printf("Socket erstellt und bereit!\n");
    
        FD_ZERO(&gesamt_sock); //gesamt_sock leeren
        FD_SET(lesesocket, &gesamt_sock); //lesesocket der Überwachung hinzufügen
    
        while(1)
        {
            ready = select( lesesocket+1, &gesamt_sock, NULL, NULL, NULL );
    
            if(FD_ISSET(lesesocket, &gesamt_sock))
            {
                rtn = recvfrom(lesesocket,buffer,sizeof(buffer),0,(SOCKADDR*)&newclient,sizeof(newclient)); //rtn Länge; buffer Nachricht
                for (i = 0; i <= MAX_CLIENTS; i++) //Gucke nach ob die IP schon bekannt ist
                {
                    if(newclient.sin_port == peer[i].port) //Sie ist bekannt
                    {
                        bekannt = i;
                        break;
                    }
                }
                if (bekannt == -1) //Wenn sie unbekannt ist
                {
                    printf("Neue Verbindung von %s\n", inet_ntoa(newclient.sin_addr));
                    for(i = 0; i < MAX_CLIENTS; i++) //Suche Speicherplatz
                    {
                        if(peer[i].used == 0)
                        {
                            peer[i].used = 1;
                            peer[i].id = i;
                            peer[i].ip = newclient.sin_addr;
                            peer[i].port = newclient.sin_port;
                            break;
                        }
                    }
    
                }
                else          //Wenn sie bekannt ist
                {
                    printf("Nachricht von ID %d:", bekannt);
                    for (i=0; i < rtn; i++)
                    {
                        printf("%X",buffer[i]);
                    }
                    //break; //Nur zum testen
                }
            }
    
            if( --ready <= 0 ) //Müssen noch Deskriptoren gelesen werden?
              continue; //Nein ...
        }
    
        return 0;
    }
    
    void error_exit(char *error_message)
    {
        fprintf(stderr,"%s: %d\n", error_message, WSAGetLastError());
        exit(EXIT_FAILURE);
    }
    
    int startWinsock(void)
    {
    	WSADATA wsa;
    	return WSAStartup(MAKEWORD(2,0),&wsa);
    }
    


  • Du wirst es nicht glauben, aber ich bin auch daram ein multiplayergame zu schreiben mit multi client... Ich habe mir desshalb überlegt wie ich die DatenPakete gestalten soll, um beide(mehrere) Benutzer unterschieden zu können.
    Ich Werde es warscheinlich mit einer kennung (z.B Hostname, oder ipadresse) die ich als Prefix, z.B erste 20Byte oder so schreibe. So könnte ich alle unterschieden. Aber vielleicht mache ^gibt es auch einen eleganteren weg.
    Ich bin mich hier am durcharbeiten (http://beej.us/guide/bgnet/)
    Gruss Binggi



  • @Binggi
    Nur leider hab ich nicht die Möglichkeit das Protokoll zu beeinflussen. Und das mit dem Prefix ist glaube ich unnötig, da man schon dies mit recvfrom bekommt. Dies muss man jedoch nur noch einbauen..

    EDIT: OK, hab den Fehler gefunden! War bei sizeof!


Anmelden zum Antworten