Laden...

Server die anzahl der zu lesenden Bytes mitteilen

Erstellt von Odin008 vor 9 Jahren Letzter Beitrag vor 9 Jahren 2.700 Views
O
Odin008 Themenstarter:in
58 Beiträge seit 2013
vor 9 Jahren
Server die anzahl der zu lesenden Bytes mitteilen

Moin,

Ich beschäftige mich momentan mit Client und Server verbindungen.
Die Idee:
Ich möchte das der Client sich am Server erstmal "vorstellen" tut
also hab ich gedacht ich bastle mir mal etwas zurecht.
Klappt auch alles soweit Daten werden vom Client gesendet und am Server empfangen.
Das Problem ist ich kann ja nie wissen wie lang z.B eine E-Mail oder ein Username ist somit kann ich den Server nicht sagen wieviele Bytes er auslesen soll. Dann kam mir die Idee "receivebuffer" beim Client fest zulegen mit der genauen länge des Strings der gesendet wird
im Server kommt dann aber nicht z.B 10Bytes raus sondern 65536 bytes d.h er liest mir 65536 Bytes aus dem Stream wovon aber nur 10 Bytes beschrieben sind die anderen sind leer.

Nun wie kann ich vom Client ausgehen senden wie viele Bytes es sind damit der Server auch die genaue Anzahl ausliest?

Hier der momentane Test Code wirrwarr
Client:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

//////////////////////////////////////////
//                CLIENT               //
/////////////////////////////////////////

namespace Client {
    class Program {
       static TcpClient clientSocket = new TcpClient();
       static NetworkStream serverStream = default(NetworkStream);
       static string readData = null;

        static void Main(string[] args) {
            Console.WriteLine("Client started.");
            Console.WriteLine("Client connecting to 127.0.0.1:2525");

            byte[] byteToServerAuth = Encoding.ASCII.GetBytes("Conan_Doyl");
            clientSocket.ReceiveBufferSize = byteToServerAuth.Length;
            clientSocket.Connect("127.0.0.1", 2525);
            serverStream = clientSocket.GetStream();

            serverStream.Write(byteToServerAuth, 0, byteToServerAuth.Length);
            serverStream.Flush();

            
            Console.ReadKey();
        }
    }
}

Server:


        public static void WaitForConnections() {
            TcpListener serverSocket = new TcpListener(IPAddress.Any, 2525);
           // List<Tuple<TcpClient, string, string>> clients = new List<Tuple<TcpClient, string, string>>();
            //clients.Add(new Tuple<TcpClient, string, string>(clientSocket, "", ""));
            TcpClient clientSocket = default(TcpClient);
            string dataFromClient = null;
            serverSocket.Start();
            while (true) {
                clientSocket = serverSocket.AcceptTcpClient();

                NetworkStream stream = clientSocket.GetStream();
                byte[] bytesAuth = new byte[10]; // Hier müsste ich die genau Bytezahl wissen die gesendet wurde.
                do {
                    stream.Read(bytesAuth, 0, clientSocket.ReceiveBufferSize); //  Hier müsste ich die genau Bytezahl wissen die gesendet wurde.
                    dataFromClient = Encoding.ASCII.GetString(bytesAuth);
                    msg(dataFromClient);
                } while (stream.DataAvailable);
                
                
            }
        }

R
228 Beiträge seit 2013
vor 9 Jahren

Du musst quasi ein eigenes "Protokoll" aufsetzen, damit dein Server versteht was der Client möchte. Ein Anfang wäre die Anzahl der Bytes die gesendet werden vorweg zu schreiben und beim Server auszulesen.

O
Odin008 Themenstarter:in
58 Beiträge seit 2013
vor 9 Jahren

Was meinst du mit ein eigenes Protokoll schreiben ?

Wenn ich das richtig verstanden habe soll ich also die Bytezahl als erstes in dem Stream schreiben und dann die Daten. Beim Server dann den Stream lesen und die Bytezahl festlegen
Doch demnach käme ich wieder auf das Problem das ich dem Stream nicht sagen kann wie Groß die Bytezahl ist ich mein wenn ich ein kleines Paket verschicke ist z.B die Bytezahl = 10 versende ich ein Großes aus einer Datenbank wäre die Bytzahl = 100
Das würde mein Problem so ja nicht lösen

R
228 Beiträge seit 2013
vor 9 Jahren

Einige dich vorher auf eine byte-Anzahl. Zum Beispiel enthalten die ersten 4bytes immer die Länge des Stream als integer. Der Server liest dann entsprechend die ersten 4 bytes und wandelt diese in einen integer um.

Aber vielleicht hat jemand anderes ja auch noch eine andere Idee.

O
Odin008 Themenstarter:in
58 Beiträge seit 2013
vor 9 Jahren

Genau das will ich ja nicht ich will mich nicht auf eine Anzahl festlegen denn ist meine Anzahl zu größ gibt er mir leere Bytes aus ist sie zu klein wirft er ne Exception oder ebend beim Buffer das er mir die Ausgabe in entwa so schreibt:
Co

na

_

Do

yl

5.657 Beiträge seit 2006
vor 9 Jahren

Hi Odin008,

es gibt doch genügend Beispiele im Internet, in denen genau dieses Szenario beschrieben wird. Schau dir bitte erstmal an, wie das dort gelöst ist, und beachte auch unseren [Hinweis] Wie poste ich richtig?, besonders Punkt 1.

Ich würde dich auch darum bitten, ein bißchen auf Rechtschreibung und Zeichensetzung zu achten. Du mußt es ja deinen Helfern nicht unnötig schwer machen.

Christian

Weeks of programming can save you hours of planning

709 Beiträge seit 2008
vor 9 Jahren

Wer sagt denn, dass die Anzahl der Bytes fest sein muss?
Du kannst sie ja so anpassen, dass du keine "leeren" Bytes hast.

O
Odin008 Themenstarter:in
58 Beiträge seit 2013
vor 9 Jahren

Ja ich habe es auch schon so versucht wie in z.B diesen Beispiel eines Chat Servers/Clients[

How to C# Chat Client ](http://csharp.net-informations.com/communications/csharp-chat-client.htm)

Das Problem dabei nur ist

byte[] outStream = Encoding.ASCII.GetByte("Blablabla" + "$");
stream.Write(outStream, 0, outStream.Length);

Läuft keine Probleme

Beim Server jedoch mit

byte[] byteFrom = new byte[10025];
stream.read(byteFrom, 0, (int)client.ReceiveBufferSize);
dataFromClient = Encoding.ASCII.GetString(bytesAuth);
msg(dataFromClient.Substring(0, dataFromClient.IndexOf('$')));

Wirft er ne Exception da byteFrom nicht so groß ist wie der ReciveBuffer.

R
228 Beiträge seit 2013
vor 9 Jahren

Nur weil immer die ersten 4bytes die Länge der gesamten gesendeten Daten beschreiben, heißt das nicht, dass du nur eine bestimmte länge haben kannst/darfst.

Du solltest du zusätzlich das Beispiel von dem MSDN mal ansehen:

TcpListener-Klasse

Insbesondere der Teil: " // Loop to receive all the data sent by the client."

O
Odin008 Themenstarter:in
58 Beiträge seit 2013
vor 9 Jahren

ok lassen wir das ihr habt anscheinend alle mein Problem nicht verstanden...
Rioma
Dein Beispiel ist genau das was ich ebend auch nicht meinte bei dem Beispiel liest er immer 256 Bytes aus dem Stream egal ob diese leer sind oder nicht desswegen will ich ja fixe Zahlen vermeiden damit er erst garnicht leere bytes auslesen kann desswegen hatte ich eher an eine lösung gedacht das ich vom Client irgendwie die länge der Daten an den Server übersenden kann und dieser dann auch nur ganz genau diese ausliest aus dem stream

Ja ich weiß das ich die leeren Bytes auch rausfiltern kann und das problem ja somit eigendlich gelöst wäre doch der interesse wegen hab ich mich halt dafür interessiert

Naja dennoch danke für diese Gespräche...

2.298 Beiträge seit 2010
vor 9 Jahren

Hallo,

wie schon korrekt von den anderen genannt wäre ein eigenes Protokoll dafür angebracht, in dem du zuerst die Länge der Nachricht mit gibst.

Häufig wird es auch wie angesprochen so gehandhabt, dass die ersten 2 oder 4 Bytes je nach Maximallänge dafür genutzt werden die Länge der Nachricht (in Byte) anzugeben.

Bevor du dann also deine Nachricht überträgst, ermittelst du zuerst die Byte-Größe (Länge) deine Nachricht hängst du anschließend an die Längenbytes an. - Wenn nun die Nachricht beim Server eintrifft liest er die Bytes die die länge definieren und wertet diese aus.

Anschließend liest der Server noch genau so viele Bytes aus dem Stream, wie du angegeben hast in der Länge. (Allerdings kenne ich auch einige Protokolle, in denen der Header [die Längenangabe] in die Gesamtlänge der Nachricht einfließen. Hier müsste man dann aufpassen, dass man die jeweilige Anzahl an Bytes ausschließt, diese wurden ja bereits gelesen.)

Ich sehe hier keine Limitierung deiner Nachrichtenlänge. Denn 4 Byte sind 32 Bit. Das heißt du kannst 2147483647 Bytes verschicken. Die Nachrichtenlänge wirst du sicher nicht erreichen.

bei dem Beispiel liest er immer 256 Bytes aus dem Stream egal ob diese leer sind oder nicht

Wieso sollten in dem Stream leere Bytes vorhanden sein? Der Stream wird genau so lange gelesen wie Bytes da sind. Wenn keine Bytes mehr da sind wird auch nicht mehr gelesen. - Übrigens erhältst du ja auch die Anzahl der gelesenen Bytes.

// EDIT:
Noch etwas, weil ich gerade deinen Code noch einmal angesehen habe. - Verabschiede dich von der Idee das ganze in einem rutsch zu lesen. - Du sprengst dir den Speicher. - Verwende hier eher eine Schleife.

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

P
157 Beiträge seit 2010
vor 9 Jahren

Hier mal ein Codebeispiel, damit du verstehst was gemeint ist:


  byte[] buffer = ....; //irgend eine Nachricht
  int i = buffer.Length;
  byte[] length = BitConverter.GetBytes(i);
  stream.Write(length);
  stream.Write(buffer);


  byte[] length = new byte[4];
  stream.Read(length,0,4);
  int i = BitConverter.ToInt32(length);
  
  byte[] buffer = new byte[i];
  stream.Read(buffer,0,i);

_
Dies ist nur ein Beispiel des obengenannten Protokolls. Es wurde nicht getestet und auch nich in einer Entwicklungsumgebung geschrieben._

82 Beiträge seit 2014
vor 9 Jahren

Hi Odin008,

die meisten hier haben Dein Problem schon richtig verstanden. Nur Deine erwünschte Lösung funktioniert so nicht. Wenn Dein Server Daten enpfangen soll, muss er wissen, was da kommt und wie es kommt. Dein Server und Client müssen die gleiche Sprache (=Protokoll) sprechen, sonst verstehen die sich nicht. Wenn Du also 347 Byte Nachricht senden willst, dann sag Deinem Server, das 347 Byte Nachricht kommen.
Auf der anderen Seite muss Dein Server auf eine Nachricht vom Client warten. Diese Nachricht ist die Zahl, der zu lesenden Byte. Die Nachricht kommt hinterher.
Dieses Protokoll musst Du Dir meist selbst bauen. Welche Struktur Du verwendest kannst Du Dir in weiten Grenzen selbst aussuchen. Aber Dein Server muss wissen, dass immer vor der Nachricht eine andere Nachricht mit fester Länge/Definition kommt, die die Länge der eigentlichen Nachricht enthält.

oder menschlich gesprochen, wenn ich (Client) Dir (Server) drei Schrauben schicke, was machst Du damit, bzw. was kommt danach?
Nur wenn Du schon weisst, dass nach ein paar Schrauben Teile folgen, die Du zusammenschrauben sollst, funktioniert unser Paketverkehr. 😉

Gruß schnelleHelga

O
Odin008 Themenstarter:in
58 Beiträge seit 2013
vor 9 Jahren

Ich möchte mich bei Euch entschuldigen für mein etwas aggresive wortwahl des letzten Posts war einfach bissl gestresst

Ja ich hatte das vieleicht auch nicht ganz verständlich erklärt
Ich weiß jetzt aufjedenfall wie ich nun vorgehen muss

Ich möchte mich bei allen bedanken die mitgeholfen haben 😃

Ich weiß zwar grad den Namen nicht doch jemand sagte die Idee alles auf einmal zu lesen wäre keine Gute Idee..Hab ich gemerkt 😄 mache es nun mit einer Schleife und führen die gelesenen datensätze später zusammen

Die Anmeldung beim Server läuft nun auch problemlos

Ich habe aber gemerkt wenn ich die Clients die verbunden sind in einer Hashtabelle oder eine List<> speichere und später versuche diesen Client (vom server aus) zu einen Disconnect zu zwingen interessiert es den Client nicht d.h der Client "denkt" noch immer das er verbunden sei

also ich trenne die Verbindung des Clients vom Server aus.
Versuche dann beim Client zu prüfen ob er noch mit dem Server verbunden ist z.B mit

TcpClient client  = new TcpClient(); // Ist nur das Beispiel damit ihr wisst was Client ist
client.close();
oder mit client.client.disconnet();

Trenne ich die verbindung vom Server aus doch prüfe ich danach am Client

If (client.connected == true) { Console.WriteLine("Client ist noch Verbunden."); }

Wenn ich den Server debugge und mir die Informationen des Clients ansehe ist connected = false und z.B

 Client.available() 

würde eine Exception werfen da er ja nicht mehr verbunden ist

Theoretisch müsste ich am Client somit ja auch nicht mehr in den Stream schreiben können also eine Exception geworfen werden doch das wird es nicht jedoch kommen die nachrichten die im Stream geschrieben wurden auch am Server nicht mehr an
Also ist er ja nicht mehr verbunden daraum verstehe ich nicht warum client.connected True ausgibt beim Client

2.298 Beiträge seit 2010
vor 9 Jahren

Eventuell hilft dir folgendes weiter, um den Disconnect im Client mitzubekommen:

http://stackoverflow.com/questions/1387459/how-to-check-if-tcpclient-connection-is-closed

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

O
Odin008 Themenstarter:in
58 Beiträge seit 2013
vor 9 Jahren

Ahh ok Danke werd ich mir mal anschauen.