none
mximale Länge von dem Buffer in einem TCP-IP Verbindung??? RRS feed

  • Frage

  • Hallo,

    ich habe einen Server, der über einer TCP-IP Verbindung Text-Dateien von einem Client empfängt und möchte gerne wissen, wie mximale Länge von dem Buffer sein soll, damit die Texte auch vollständig empfangen werden? Ich habe momentan byte[] buffer = new byte[Int32.MaxValue];  Ist das richtig?

    mein Code sieht wie folgt aus:

    Client:

    tcpclnt = new TcpClient();
    string stream = "";
    tcpclnt.Connect(IPv4, Port);
    Stream stm = tcpclnt.GetStream();
    string eintrag = "";
    int trennung = 0;
    
    foreach (System.IO.FileInfo file3 in filelist)
    {
    	trennung += 1;
        if (file3.Name != "ersteTeil.txt")
        {
        	StreamReader file = new StreamReader(@"\Backup\Data\" + file3.Name, System.Text.Encoding.Default);
            eintrag += file.ReadToEnd();
            file.Close();
            int laeng = text.Length;
            string ausg = text;
            stream += ausg;
            if (trennung < filelist.Length)
            stream += "%";
        }
    }
    
    ASCIIEncoding asen = new ASCIIEncoding();
    byte[] ba = asen.GetBytes(stream);
    stm.Write(ba, 0, ba.Length);
    byte[] message = new byte[100];
    int bytesRead = 0;
    
    bytesRead = stm.Read(message, 0, 100);
    if (bytesRead > 0)
    {
    	label1.Text = "   Daten sind erfolgreich übertragen!";
    }

    Server:

    int received = 0;
    byte[] buffer;
    
    do
    {
    	try
        {
        	buffer = new byte[Int32.MaxValue];
            received = socket.Receive(buffer);
                    
            UTF8Encoding encoding = new UTF8Encoding();
            empfangen += encoding.GetString(buffer, 0, received);
            string toSend = "ACK";
            socket.Send(encoding.GetBytes(toSend));
        }
        catch (Exception exp)
        {
        	break;
        }
    }
    while (received != 0);


    Gruesse, NUNUI

    Mittwoch, 17. August 2016 12:43

Antworten

  • Hallo Nunui,

    das Problem bei TCP Verbindungen ist generell, dass nicht garantiert ist, das alle Daten in einem Lesevorgang kommen. Denn die Paketgröße, die in einem Rutsch übertragen werden kann, hängt von dem unterliegenden Netzwerk ab - was zumindest in einem WAN alles mögliche sein kann. Die haben unterschiedliche MTUs, was wiederum zu einer Fragmentierung der Pakete in mehrere führen kann, die nicht zwangsläufig passend ankommen. Receive versucht maximal soviel Daten zu lesen wie der Puffer hergibt - ansonsten wird blockiert bis der ReceiveTimeout erreicht ist.

    Somit musst Du irgend eine Kennung haben, wann ein Empfangsvorgang regulär beendet ist. Das kann entweder durch eine vorangestellte Länge oder auch durch ein Endekennzeichen (bei Texten wie bei HTTP oft eine oder mehrere Zeilenvorschübe). Oder aber Du schließt den Socket am Ende.

    Zweites Problem sind Zeichenkodierungen, die aus mehreren Bytes bestehen können, bei UTF-8 bis zu 4 Bytes. So kann es durch genannte Fragmentierung passieren, dass das letzte Zeichen nicht vollständig übertragen ist. Was Dein encoding.GetString auf die Nase fallen lässt.

    Besser ist man verwendet den NetworkStream und setzt darüber einen StreamReader. Der verwendet einen Decoder für den Zeichensatz um zu erkennen, ob ein Zeichen vollständig empfangen wurde.

    Gruß Elmar

    Mittwoch, 17. August 2016 15:43
    Beantworter
  • Hallo Nunui,

    Elmar hat einig Dinge genannt, die sehr wichtig sind.

    Bei deinem Code fallen mir ein paar Dinge auf, die ich gerne ansprechen möchte:

    - Zum einen fällt mir auf, dass Du in einer Schleife eine Instanz von einem Array erzeugst. Das ist nicht ganz so toll, da Du so dem GC einiges an Arbeit gibst. Da wäre es einfacher, den Buffer nur einmal zu erzeugen und einfach immer wieder zu verwenden.

    - Bei deinem ersten Code versuchst Du evtl. die gelesenen Bytes zurück zu geben? Du zählst, wie oft Du gelesen hast, aber die Anzahl der gelesenen Bytes ist ja nicht 1024 - es kann ja 10 mal jeweils nur 2 Bytes gelesen worden sein. Da wäre es also sinnvoller die Anzahl der gelesenen Bytes aufzuaddieren.

    Die Idee von Elmar mit dem StreamReader auf dem NetworkStream ist gut, so man die blockierenden Zugriffe haben möchte. Ich selbst bevorzuge, es zu keinen blockierenden Aufrufen kommen zu lassen indem ich zuerst prüfe, ob Daten anliegen und nur dann wirklich die Daten lese.

    Bezüglich Tcp Verbindungen kann ich Dir zwei Dinge aufzeigen, die evtl hilfreich sind:

    a) Tcp/Ip Verbindung / Server mittels TAP (async / await): 
    http://social.technet.microsoft.com/wiki/contents/articles/30394.simple-multi-user-tcpip-client-server-using-tap.aspx 

    b) Ich selbst habe den Code für einen Client / Server mal mittels eines dedizierten Threads geschrieben, der dann entsprechende Events auslöst. Das ist in der NeitzelLib auf Codeplex und die beiden Hauptklassen wären TcpIpClientConnection und TcpIpServerConnection.

    Mit den besten Grüßen,

    Konrad

    Donnerstag, 18. August 2016 08:58

Alle Antworten

  • Hallo.

    Normalerweise geht man nicht davon aus, dass der Buffer SO groß sein soll... Das frisst zu viel Memory...

    Versuche lieber einen kleinen Puffer zu machen, den aber in einer Schleife auszunehmen.

    Beim TCP Stream gibt es die Read() Methode, die dir zurück gibt, wie viele Bytes gelesen wurden.

    Hier ist ein Beispiel für solch ein Vorgehen (auf zwei Streams basierend, Input und Output):

    public static void CopyStream(Stream input, Stream output)
    {
        byte[] buffer = new byte[8 * 1024];
        int len;
        while ( (len = input.Read(buffer, 0, buffer.Length)) > 0)
        {
            output.Write(buffer, 0, len);
        }    
    }

    Du siehst, solange beim letzten Aufruf von Read() Bytes gelesen wurden (len > 0), dann ist der Quell-Stream noch nicht leer, hat also noch Daten.

    Wenn len = 0 ist, ist der Quell-Stream zuende, und das Lesen kann beendet werden...

    Das ist die normale Vorgehensweise.

    Hoffe du verstehst, was ich damit meine?


    © 2016 Thomas Roskop
    Germany //  Deutschland

    Mittwoch, 17. August 2016 14:16
  • Danke Thomas für Deine Antwort!

    Würde das vielleicht doch auch so funktionieren?

    int maxBuffer()
    {
       int received = 0;
       byte[] buffer;
       int count = 0;
    
       do
       {
          count++;
          buffer = new byte[1024];
          received = socket.Receive(buffer);          
       }
       while (received != 0);
    
       return (count * 1024);
    }
    
    
    void ReceiveHandler()
    {
       int received = 0;
       byte[] buffer;
    
       do
       {
          try
          {
             buffer = new byte[maxBuffer()];
             received = socket.Receive(buffer);
                    
             UTF8Encoding encoding = new UTF8Encoding();
             empfangen += encoding.GetString(buffer, 0, received);
             string toSend = "ACK";
             socket.Send(encoding.GetBytes(toSend));
          }
          catch (Exception exp)
          {
        	  break;
          }
    }
    while (received != 0);


    Gruesse, NUNUI

    Mittwoch, 17. August 2016 15:12
  • Hallo Nunui,

    das Problem bei TCP Verbindungen ist generell, dass nicht garantiert ist, das alle Daten in einem Lesevorgang kommen. Denn die Paketgröße, die in einem Rutsch übertragen werden kann, hängt von dem unterliegenden Netzwerk ab - was zumindest in einem WAN alles mögliche sein kann. Die haben unterschiedliche MTUs, was wiederum zu einer Fragmentierung der Pakete in mehrere führen kann, die nicht zwangsläufig passend ankommen. Receive versucht maximal soviel Daten zu lesen wie der Puffer hergibt - ansonsten wird blockiert bis der ReceiveTimeout erreicht ist.

    Somit musst Du irgend eine Kennung haben, wann ein Empfangsvorgang regulär beendet ist. Das kann entweder durch eine vorangestellte Länge oder auch durch ein Endekennzeichen (bei Texten wie bei HTTP oft eine oder mehrere Zeilenvorschübe). Oder aber Du schließt den Socket am Ende.

    Zweites Problem sind Zeichenkodierungen, die aus mehreren Bytes bestehen können, bei UTF-8 bis zu 4 Bytes. So kann es durch genannte Fragmentierung passieren, dass das letzte Zeichen nicht vollständig übertragen ist. Was Dein encoding.GetString auf die Nase fallen lässt.

    Besser ist man verwendet den NetworkStream und setzt darüber einen StreamReader. Der verwendet einen Decoder für den Zeichensatz um zu erkennen, ob ein Zeichen vollständig empfangen wurde.

    Gruß Elmar

    Mittwoch, 17. August 2016 15:43
    Beantworter
  • Hallo Nunui,

    Elmar hat einig Dinge genannt, die sehr wichtig sind.

    Bei deinem Code fallen mir ein paar Dinge auf, die ich gerne ansprechen möchte:

    - Zum einen fällt mir auf, dass Du in einer Schleife eine Instanz von einem Array erzeugst. Das ist nicht ganz so toll, da Du so dem GC einiges an Arbeit gibst. Da wäre es einfacher, den Buffer nur einmal zu erzeugen und einfach immer wieder zu verwenden.

    - Bei deinem ersten Code versuchst Du evtl. die gelesenen Bytes zurück zu geben? Du zählst, wie oft Du gelesen hast, aber die Anzahl der gelesenen Bytes ist ja nicht 1024 - es kann ja 10 mal jeweils nur 2 Bytes gelesen worden sein. Da wäre es also sinnvoller die Anzahl der gelesenen Bytes aufzuaddieren.

    Die Idee von Elmar mit dem StreamReader auf dem NetworkStream ist gut, so man die blockierenden Zugriffe haben möchte. Ich selbst bevorzuge, es zu keinen blockierenden Aufrufen kommen zu lassen indem ich zuerst prüfe, ob Daten anliegen und nur dann wirklich die Daten lese.

    Bezüglich Tcp Verbindungen kann ich Dir zwei Dinge aufzeigen, die evtl hilfreich sind:

    a) Tcp/Ip Verbindung / Server mittels TAP (async / await): 
    http://social.technet.microsoft.com/wiki/contents/articles/30394.simple-multi-user-tcpip-client-server-using-tap.aspx 

    b) Ich selbst habe den Code für einen Client / Server mal mittels eines dedizierten Threads geschrieben, der dann entsprechende Events auslöst. Das ist in der NeitzelLib auf Codeplex und die beiden Hauptklassen wären TcpIpClientConnection und TcpIpServerConnection.

    Mit den besten Grüßen,

    Konrad

    Donnerstag, 18. August 2016 08:58
  • Hi Elmar,

    sorry, dass ich hier jetzt kurz Off Topic werde, aber evtl. könntest Du Dich einmal kurz per Email an mich wenden? Es gibt da eine neue Aktion bezüglich Forum Ninjas von Ed Price und ich bin am überlegen, da auch einen Beitrag zu einzureichen. Da wäre es sehr nett, wenn wir dazu kurz in Kontakt kommen könnten.

    Du erreichst mich per Email unter konrad  at neitzel.de. 

    Vielen Dank schon jetzt.

    Viele Grüße,

    Konrad Neitzel

    Donnerstag, 18. August 2016 09:10