C++ Socket Server / Client



  • Moin!

    Ich habe folgende Programme (siehe unten) geschrieben.
    Wie muss ich den Server bzw. den Client umschreiben, dass der Server alle Nachrichten an alle Clienten weiterleitet?
    Außerdem soll der Server alle Aktionen (Nachrichten, Clientenadressen etc) speichern (auf die Festplatte).

    Die Clienten sollen die Nachrichten empfangen und senden können? (C++ 11 Thread?).

    Danke

    Server

    //#pragma comment( lib,"ws2_32.lib" )
    #include <windows.h>
    
    #include <winsock2.h> // bei manchan compilern muss man nur windows.h includieren (zB VC++)
    
    #include <stdio.h>
    
    #define MAX_CLIENTS 10
    
    int startWinsock(void) {
        WSADATA wsa;
        return WSAStartup(MAKEWORD(2,0),&wsa);
        }
    
    int main() {
        long rc;
        SOCKET acceptSocket;
        //SOCKET connectedSocket;
        SOCKADDR_IN addr;
        char buf[256];
        char buf2[300];
        // zusätzliche Variabeln
        FD_SET fdSet;
        SOCKET clients[MAX_CLIENTS];
        int i;
    
        // Winsock starten
        rc=startWinsock();
        if(rc!=0) {
            printf("Fehler: startWinsock, fehler code: %ld\n",rc);
            return 1;
        } else {
            printf("Winsock gestartet!\n");
        }
    
        // Socket erstellen
        acceptSocket=socket(AF_INET,SOCK_STREAM,0);
        if(acceptSocket==INVALID_SOCKET) {
            printf("Fehler: Der Socket konnte nicht erstellt werden, fehler code: %d\n",WSAGetLastError());
            return 1;
        } else {
            printf("Socket erstellt!\n");
        }
    
        // Socket binden
        memset(&addr,0,sizeof(SOCKADDR_IN));
        addr.sin_family=AF_INET;
        addr.sin_port=htons(12345);
        addr.sin_addr.s_addr=INADDR_ANY; // gewisse compiler brauchen hier ADDR_ANY
        rc=bind(acceptSocket,(SOCKADDR*)&addr,sizeof(SOCKADDR_IN));
        if(rc==SOCKET_ERROR) {
            printf("Fehler: bind, fehler code: %d\n",WSAGetLastError());
            return 1;
        } else {
            printf("Socket an port 12345 gebunden\n");
        }
    
        // In den listen Modus
        rc=listen(acceptSocket,10);
        if(rc==SOCKET_ERROR) {
            printf("Fehler: listen, fehler code: %d\n",WSAGetLastError());
            return 1;
        } else {
            printf("acceptSocket ist im listen Modus....\n");
        }
    
        for(i=0;i<MAX_CLIENTS;i++) {
            clients[i]=INVALID_SOCKET;
        }
    
        while(1) {
            FD_ZERO(&fdSet); // Inhalt leeren
            FD_SET(acceptSocket,&fdSet); // Den Socket der verbindungen annimmt hinzufügen
    
            // alle gültigen client sockets hinzufügen (nur die die nicht INVALID_SOCKET sind)
            for(i=0;i<MAX_CLIENTS;i++) {
                if(clients[i]!=INVALID_SOCKET) {
                    FD_SET(clients[i],&fdSet);
                }
            }
    
            rc=select(0,&fdSet,NULL,NULL,NULL);
            // nicht vergessen den ersten parameter bei anderen betriebssystem anzugeben
            if(rc==SOCKET_ERROR) {
                printf("Fehler: select, fehler code: %i\n",WSAGetLastError());
                return 1;
            }
    
            // acceptSocket is im fd_set? => verbindung annehmen (sofern es platz hat)
            if(FD_ISSET(acceptSocket,&fdSet)) {
                // einen freien platz für den neuen client suchen, und die verbingung annehmen
                for(i=0;i<MAX_CLIENTS;i++) {
                    if(clients[i]==INVALID_SOCKET) {
                        clients[i]=accept(acceptSocket,NULL,NULL);
                        printf("Neuen Client angenommen (%d)\n",i);
                        break;
                    }
                }
            }
            // prüfen wlecher client sockets im fd_set sind
            for(i=0;i<MAX_CLIENTS;i++) {
                if(clients[i]==INVALID_SOCKET) {
                    continue; // ungültiger socket, d.h. kein verbunder client an dieser position im array
                }
                if(FD_ISSET(clients[i],&fdSet)) {
                    rc=recv(clients[i],buf,256,0);
                    // prüfen ob die verbindung geschlossen wurde oder ein fehler auftrat
                    if(rc==0 || rc==SOCKET_ERROR) {
                        printf("Client %d hat die Verbindung geschlossen\n",i);
                        closesocket(clients[i]); // socket schliessen
                        clients[i]=INVALID_SOCKET; // seinen platz wieder freigeben
                    } else {
                        buf[rc]='\0';
                        // daten ausgeben und eine antwort senden
                        printf("Client %d hat folgendes gesandt: %s\n",i,buf);
                        // antwort senden
                        sprintf(buf2,"Du mich auch %s\n",buf);
                        send(clients[i],buf2,(int)strlen(buf2),0);
                    }
                }
            }
        }
    }
    

    Client

    #include <windows.h>
    #include <winsock2.h>
    #include <stdio.h>
    
    //Prototypen
    int startWinsock(void);
    
    int main()
    {
      long rc;
      SOCKET s;
      SOCKADDR_IN addr;
      char buf[256];
    
      // Winsock starten
      rc=startWinsock();
      if(rc!=0)
      {
        printf("Fehler: startWinsock, fehler code: %d\n",rc);
        return 1;
      }
      else
      {
        printf("Winsock gestartet!\n");
      }
    
      // Socket erstellen
      s=socket(AF_INET,SOCK_STREAM,0);
      if(s==INVALID_SOCKET)
      {
        printf("Fehler: Der Socket konnte nicht erstellt werden, fehler code: %d\n",WSAGetLastError());
        return 1;
      }
      else
      {
        printf("Socket erstellt!\n");
      }
    
      // Verbinden
      memset(&addr,0,sizeof(SOCKADDR_IN)); // zuerst alles auf 0 setzten
      addr.sin_family=AF_INET;
      addr.sin_port=htons(12345); // wir verwenden mal port 12345
      addr.sin_addr.s_addr=inet_addr("127.0.0.1"); // zielrechner ist unser eigener
    
      rc=connect(s,(SOCKADDR*)&addr,sizeof(SOCKADDR));
      if(rc==SOCKET_ERROR)
      {
        printf("Fehler: connect gescheitert, fehler code: %d\n",WSAGetLastError());
        return 1;
      }
      else
      {
        printf("Verbunden mit 127.0.0.1..\n");
      }
    
      // Daten austauschen
      while(rc!=SOCKET_ERROR)
      {
        printf("\nZeichenfolge eingeben [max 256]: ");
        gets(buf);
        send(s,buf,strlen(buf),0);
        rc=recv(s,buf,256,0);
        if(rc==0)
        {
          printf("Server hat die Verbindung getrennt..\n");
          break;
        }
        if(rc==SOCKET_ERROR)
        {
          printf("Fehler: recv, fehler code: %d\n",WSAGetLastError());
          break;
        }
        buf[rc]='\0';
        printf("\nServer antwortet: %s\n",buf);
      }
      closesocket(s);
      WSACleanup();
      return 0;
    }
    
    int startWinsock(void)
    {
      WSADATA wsa;
      return WSAStartup(MAKEWORD(2,0),&wsa);
    }
    


  • Du hast doch bei deinem Server eine Struktur, die alle sockets von eingegangenen Verbindungen enthält.

    Wenn du jetzt also in deiner Nachrichtenschleife von einem der Sockets etwas zugeschickt bekommst, dann sendest du halt als Konsequenz diese Nachricht an alle Sockets in dieser Struktur weiter. Außer natürlich an den, von dem die Nachricht kommt.
    So fern ich mich erinnere (länger her) werden bei Clients, die die Verbindungen trennen auch Nachrichten mit irgendeinem Sondercodd geschickt - die natürlich nicht weiter schicken sondern entsprechend verarbeiten.

    Die Problematik bei "Clients sollen empfangen und senden können" verstehe ich nicht.
    Du horchst auf den Server (permanent, non blocking) und das senden wird wohl als Resultat von irgendeiner GUI Aktion geschehen.



  • m.sc.m schrieb:

    Ich habe folgende Programme (siehe unten) geschrieben.

    Wirklich?

    m.sc.m schrieb:

    Wie muss ich den Server bzw. den Client umschreiben, dass der Server alle Nachrichten an alle Clienten weiterleitet?

    Wenn es dein Programm ist, solltest du das wissen.
    Aber schau mal in Zeile 115ff. Die Achricht schickst du nicht nur an client[i] sondern auch an alle anderen gültigen Einträge aus client.

    m.sc.m schrieb:

    Außerdem soll der Server alle Aktionen (Nachrichten, Clientenadressen etc) speichern (auf die Festplatte).

    Da machst du an derselben Stelle.

    m.sc.m schrieb:

    Die Clienten sollen die Nachrichten empfangen und senden können? (C++ 11 Thread?).

    Tun sie das nicht?
    Oder möchtest du auf das Warten bei der Eingabe verzichten?.
    Nebenbei ist gets böse und abgekündigt. Das verwendet man seit 25 Jahren nicht mehr.

    Und mit C++ hat der Code nichts zu tun. Das ist reines C.



  • Also in Zeile 62 wird die Eingabe des Clienten (Benutzers) abgefangen und anschließend an den Server gesendet.

    In Zeile 72 wird die (eventuelle) Nachricht vom Server ausgegeben.
    Hier möchte ich aber, dass alle Nachrichten, die vor, bei und direkt beim absenden geschickte Nachricht sofort ausgegeben wird und nicht "irgendwann" bzw "teilweise" danach.

    Danke



  • DirkB schrieb:

    m.sc.m schrieb:

    Ich habe folgende Programme (siehe unten) geschrieben.

    Wirklich?

    m.sc.m schrieb:

    Wie muss ich den Server bzw. den Client umschreiben, dass der Server alle Nachrichten an alle Clienten weiterleitet?

    Wenn es dein Programm ist, solltest du das wissen.
    Aber schau mal in Zeile 115ff. Die Achricht schickst du nicht nur an client[i] sondern auch an alle anderen gültigen Einträge aus client.

    m.sc.m schrieb:

    Außerdem soll der Server alle Aktionen (Nachrichten, Clientenadressen etc) speichern (auf die Festplatte).

    Da machst du an derselben Stelle.

    m.sc.m schrieb:

    Die Clienten sollen die Nachrichten empfangen und senden können? (C++ 11 Thread?).

    Tun sie das nicht?
    Oder möchtest du auf das Warten bei der Eingabe verzichten?.
    Nebenbei ist gets böse und abgekündigt. Das verwendet man seit 25 Jahren nicht mehr.

    Und mit C++ hat der Code nichts zu tun. Das ist reines C.

    Ich hab es mit 2 Clients getestet und sie erhalten nur ihre eigenen Nachrichten und erhalten keine anderen Nachrichten....



  • m.sc.m schrieb:

    Ich hab es mit 2 Clients getestet und sie erhalten nur ihre eigenen Nachrichten und erhalten keine anderen Nachrichten....

    liest du:

    DirkB schrieb:

    m.sc.m schrieb:

    Wie muss ich den Server bzw. den Client umschreiben, dass der Server alle Nachrichten an alle Clienten weiterleitet?

    Wenn es dein Programm ist, solltest du das wissen.
    Aber schau mal in Zeile 115ff. Die Nachricht schickst du nicht nur an client[i] sondern auch an alle anderen gültigen Einträge aus client.



  • Entweder habe ich grad zu viel Alkohol intus 🤡

    aber wie muss ich dann es ändern, wenn zum Beispiel der client 0, Client 1 und Client 2 die Nachrichten der anderen Clienten auch empfangen?

    Danke



  • Dieser Code kommt nach Zeile 113, aber noch im else-Zweig

    for(j=0;j<MAX_CLIENTS;j++) {
                if (j==i) continue;  // aktueller client?
                if(clients[j]==INVALID_SOCKET) {
                    continue; // ungültiger socket, d.h. kein verbunder client an dieser position im array
                }
                send(clients[j],buf,(int)strlen(buf),0);
              }
    


  • Falsches Subforum - ist WinAPI und nicht C++.



  • Wenn du eine nachricht an alle senden willst musst du das mit einer schleife machen wie die hier:

    else {
               buf[rc]='\0';
               // daten ausgeben und eine antwort senden
               printf("Client %d hat folgendes gesandt: %s\n",i,buf);
               //Jetzt an alle clients die nachricht schicken
               for(int a = 0;a < MAX_CLIENTS;a++)
               {
                   if(a != i)
                   {
                     //Nachricht nur schicken wenn es nicht der sender ist
                     send(clients[a],buf,(int)strlen(buf),0);
                   }
               }
         }
    

    Wenn der client empfangen und gleichzeitig die eingabe evrarbeiten soll musst du threads benutzen mit der funktion CreateThread geht das ganz gut 😃 mehr infos über threads gibt google 😃

    Und wenn du die nachrichten von den clients speischern willst scheib sie in eine
    txt datei dazu gibt viel in google

    sry wegen der rechtschreibung 🙄



  • helper_D schrieb:

    Wenn der client empfangen und gleichzeitig die eingabe evrarbeiten soll musst du threads benutzen

    Mit kbhit() (ist ja Windows und C) sollte das auch gehen. (Nicht die Threads, aber das nicht-blocklierende lesen von der Konsole)

    Wenn du aber mal eine GUI dafür machen willst, musst du das Programm eh umbauen.


Anmelden zum Antworten