Datenversand durch send() und recv()



  • Hallo Leute,

    Ich habe einen kleinen Code für einen Sendecomputer und einen Empfangscomputer. Getestet habe ich alles auf einem Computer. Die Datei, die von dem Empfangscomputer erstellt wird, ist so groß wie das Original. Man kann die Datei jedoch nicht mehr öffnen (Bbiespielsweise eine Exe oder Word- Datei).

    Empfangscomputer:

    void __fastcall TForm1::DateiEmpfangen(String ZielDateiPfad, int DateiGroesse)
    {
      // Speicher für die Datei anlegen:
      char *Daten;
    
      char SendeBuffer[16];
      if((Daten = (char *) malloc(DateiGroesse)) == NULL)
      {
        Label1->Caption = "Der Speicherplatz reicht für die Daten nicht aus!";
        SendeBuffer[0] = 0; // Durch 0 wird dem Sendecomputer angezeigt, dass der Empfangscomputer für den Datenversand nicht bereit ist.
        send(ClientSock, SendeBuffer, 1, 0);
      }
      else
      {
        Label1->Caption = "Der Speicherplatz ist ausreichend für die Daten!";
        SendeBuffer[0] = 1;  // Durch 1 wird dem Sendecomputer agezeigt, dass der Empfangscomputer für den Datenversand bereit ist.
        send(ClientSock, SendeBuffer, 1, 0);
      }
    
      // Wenn alles bereit ist, kann auf die Datenpäckchen gewartet werden. Jeder Empfang wird extra bestätigt!!!
      if(SendeBuffer[0] == 1)
      {
        int len;
        int anzahl;
        #define ANZAHL  1024  // Anzahl der Zeichen je Päckchen!!!
        len = 0;
        while(len < DateiGroesse)
        {
          anzahl = (len + ANZAHL < DateiGroesse) ? ANZAHL : DateiGroesse - len;
          if((anzahl = recv(ClientSock, Daten + len, anzahl, 0)) == SOCKET_ERROR)
          {
            Label1->Caption = "Es wurden keine Daten empfangen!";
            free(Daten);
          }
    
          len += anzahl;
    
          // Bestätigung an Sendecomputer senden
          send(ClientSock, SendeBuffer, 1, 0);  // Erneut wird 1 gesendet
        }
    
        // Die Daten wurden empfangen:
        Label1->Caption = "Es wurden " + IntToStr(len) + " Bytes empfangen!";
    
        // Die Datei abspeichern:
        FILE *Datei;
        if((Datei = fopen(ZielDateiPfad.c_str(), "wb")) == NULL)
        {
          Label1->Caption = "Die Daten können nicht gespeichert werden!";
        }
    
        fwrite(Daten, DateiGroesse, 1, Datei);
        fclose(Datei);
    
        free(Daten);
    
        // Der Datenempfang wurde beendet:
        Application->MessageBox("Der Datenempfang wurde abgeschlossen!", "Datenempfang beendet", 0+64);
      }
    }
    

    Sendecomputer:

    void __fastcall TForm1::DateiSenden(String DateiPfad, int DateiGroesse)
    {
      char Buffer[16];
    
      char *Daten;
      // Den Speicher reservieren:
      if((Daten = (char *) malloc (DateiGroesse)) == NULL)
      {
        Label5->Caption = "Es ist nicht genügend Speicherplatz vorhanden!";
      }
    
      // darauf warten. dass der Empfangscomputer das Freizeichen gibt:
      recv(Sock, Buffer, 1, 0);
    
      if(!Buffer[0])
      {
        Label5->Caption = "Es gibt nicht genügend Speicherplatz auf dem Empfangscomputer!";
      }
    
      // Wenn alles OK, dann Daten an den Empfangscomputer senden:
      if(Buffer[0] == 1)
      {
        int len = 0;
        int anzahl;
        #define ANZAHL  1024  // Anzahl der Zeichen je Päckchen
        while(len < DateiGroesse)
        {
          anzahl = (len + ANZAHL < DateiGroesse) ? ANZAHL : DateiGroesse - len;
    
          if((anzahl = send(Sock, Daten + len, anzahl, 0)) == SOCKET_ERROR)
          {
            Label5->Caption = "Fehler beim senden der Daten!";
          }
    
          len += anzahl;
    
          // Auf die Bestätigung des Empfangscomputers warten:
          recv(Sock, Buffer, 1, 0);
        }
    
        Label5->Caption = "Der Datenversand ist beendet!";
      }
    }
    

    Sieht vielleicht jemand von euch den Fehler? Wenn ja, dann helft mir bitte.

    Vielen Dank,
    Euer Entertainer



  • Ich kann nicht erkennen, wo du beim Senden die Datei in den Sendepuffer "Daten" kopierst. Und woher weiß dein Empfänger überhaupt, wie groß die Datei ist? Meiner Meinung nach fehlt hier ein Protokoll. Die "0" und die "1", die du als Empfangs- bzw. Sendebestätigung auswertest, könnte im Extremfall auch schon beim Zusammenstöpseln erscheinen.

    Rob'



  • Warum nutzt du nicht die TServerSocket/TClientSocket Komps vom Builder? Damit wäre das senden/empfangen schon ziemlich einfach.

    Da ist alles drin was du brauchst. 😉



  • Entertainer: mir ist noch etwas eingefallen: das Senden in 1k-Blöcken heißt nicht, dass die Daten in 1k-Blöcken über's Netz gehen, sondern nur die Datenübergabe an die Socketpuffer erfolgt in 1k-Blöcken, dasselbe gilt beim Empfang: auch hier liest du die internen Socketpuffer nur in 1k-Blöcken aus, es können wesentlich mehr Daten in den Empfangspuffern vorhanden sein, die dann mit mehreren recv-Aufrufen ausgelesen werden müssen

    PuppetMaster2k: die Builder-Komponenten lösen dieses Problem nicht, auch hier musst du dich um denselben Kram selbst kümmern

    Rob'



  • Schon vergessen, dass wir das damals nach WinAPI verschoben haben?
    Also nochmal das ganze.



  • Rob' Die Größe der Datei wird ermittelt und von dem Sendecomputer an den Empfangscomputer gesendet, bevor die ganze Aktion losgeht. Die ermittelte Wert der Größe steht in den unten angegeben Funktionen in

    int DateiGroesse
    

    Könnt ihr mir vielleicht jetzt weiterhelfen?

    Euer Entertainer



  • Dein Protokoll mit den Datenpäckchen und der Empfangsquittung verstehe ich nicht: Mit send kopierst du deine Daten in socketinterne Puffer. Wie und in welcher Größe die Daten über dein Netzwerk gesendet werden, ist Sache des Betriebssystems. D.h. deine Daten können mit ganz anderer Päckchengröße auf die Reise gehen und beim Empfänger in den socketinternen Empfangspuffern landen. Bsp.: Du kopierst 2k mit send. Übers Netz gehen die Daten aber in 1k-Blöcken. Auf der Empfängerseite liest du jetzt mit recv einen 1k-Block, den du dann quittierst. Aber eigentlich darfst du erst nach 2k quittieren. Du mußt also auf der Empfängerseite solange recv ausführen, bis du deinen 2k-Block zusammenhast.

    Wenn ich dich richtig verstanden habe, hast du schon soetwas wie ein Protokoll: Du unterscheidest zwischen Übermittlung der Dateigröße und dem Senden der Daten selbst. Warum kopierst du dann deine zu sendende Datei (mit send) nicht ohne Quittierung der einzelnen Datenpäckchen, und führst auf der Empfängerseite solange recv aus, bis du x Bytes empfangen hast (x=Dateigröße)?

    Ansonsten könntest du mit einem Dateibetrachter die Inhalte der Dateien auf dem Sende- und auf dem Empfangscomputer vergleichen, um herauszubekommen, wo die Unterschiede sind.

    Rob'


Anmelden zum Antworten