Laden...

Problem: Übertragung TCP Client-->Server

Erstellt von Derok vor 13 Jahren Letzter Beitrag vor 13 Jahren 2.673 Views
D
Derok Themenstarter:in
19 Beiträge seit 2011
vor 13 Jahren
Problem: Übertragung TCP Client-->Server

Hey,

bin im moment beim umsteigen von Java auf c# und hab mich dort schon öfters mit netzwerkprogrammierung beschäftigt, allerdings ist das in c# ein tick komplizierter ^^.
Aufjedenfall hab ich bereits ein Programm geschrieben, dort hat es ohne Probleme funktioniert, aber nun hab ich grad ein Durchhänger und find den Wald vor lauter Bäumen nicht wenn man es so nennen will 😉.

Server-code um den es sich handelt:


public void HandleClientComm(object client)
        {
            work.meldung(false, "Thread hat client übernommen");
            //TcpClient tcpClient = (TcpClient)client;
            //NetworkStream clientStream = tcpClient.GetStream();
            Socket clients = (Socket)client;
            ASCIIEncoding encoder = new ASCIIEncoding();
            work.meldung(false, "Thread initialisiert");
            try
            {
                //Wartet auf income vom Client
                byte[] bname = new byte[4096];
                work.meldung(false, "Warte auf name");
                //int bytesRead1 = clientStream.Read(bname, 0, 4096);
                clients.Receive(bname);
                work.meldung(false, "name empfangen");
                byte[] bpasswort = new byte[4096];
                work.meldung(false, "warte auf pw");
                //int bytesRead2 = clientStream.Read(bpasswort, 0, 4096);
                clients.Receive(bpasswort);
                work.meldung(false, "pw empfangen");
                String name = BitConverter.ToString(bname);
                work.meldung(false, name);
                String passwort = BitConverter.ToString(bpasswort);
                work.meldung(false, passwort);
                int idss = 1328;
                String ids = ""+idss;
                byte[] id = encoder.GetBytes(ids);
                //clientStream.Write(id, 0, id.Length);
                clients.Send(id);
            }
            catch (SocketException e)
            {
                work.meldung(true, "Ein Socket-Fehler bei einem Login-Client-Thread ist aufgetreten");
                work.meldung(false, "" + e);
            }
            catch (IOException e)
            {
                work.meldung(true, "Ein IO-Fehler bei einem Login-Client-Thread ist aufgetreten");
                work.meldung(false, "" + e);
            }
            //clientStream.Close();
            //tcpClient.Close();
            clients.Close();
        }

Client Code um den es sich handelt:


private void BTlogin_Click(object sender, EventArgs e)
        {
                String name = TBname.Text;
                String pw = TBpw.Text;
                String host = "127.0.0.1";
                int port = 3115;
                ASCIIEncoding encoder = new ASCIIEncoding();
                byte[] bname = new byte[4096];
                bname = encoder.GetBytes(name);
                byte[] bpw = new byte[4096];
                bpw = encoder.GetBytes(pw);
                TBfehler.Text = name + "/" + pw;
                IPAddress ipo = IPAddress.Parse(host);
                IPEndPoint ipEo = new IPEndPoint(ipo, port);
                Socket socket = new Socket(ipo.AddressFamily,
                                               SocketType.Stream,
                                               ProtocolType.Tcp);
                try
                {
                    socket.Connect(ipEo);
                    socket.Send(bname);
                    socket.Send(bpw);
                    byte[] message = new byte[4096];
                    socket.Receive(message);
                    TBfehler.Text = "Nachricht erhalten";
                    int id = BitConverter.ToInt32(message, 0);
                    TBid.Text = "ID: " + id;
                    socket.Close();
                }
                catch (SocketException k)
                {
                    TBfehler.Text = "" + k;
                }
                catch (ArgumentOutOfRangeException k)
                {
                    TBfehler.Text = "" + k;
                }
                catch (ObjectDisposedException k)
                {
                    TBfehler.Text = "" + k;
                }
                catch (ArgumentNullException k)
                {
                    TBfehler.Text = "" + k;
                }
        }

Nun zur sache, das was auskommentiert habe bei dem server ist eine andere methode die ich mal in nen tutorial gesehen hab, dort bin ich aber bis zum gleichen punkt gekommen.
Verbindung baut es erfolgreich auf und der nick name wird übertragen.
Auf serverseite ist der letzte befehl den er erfolgreich ausführt
work.meldung(false, "warte auf pw");
auf clientseite entsprechend
socket.Send(bname);
weiß einer von euch warum es dann nciht weitergeht?
Hoffe ihr könnt mir helfen

S
341 Beiträge seit 2008
vor 13 Jahren

Servus,

versuch doch mal Receive mit byte[8192] und schau mal ob beides drin steht 😉...

EDIT: PS: Schau dir doch mal die TcpClient Klasse an... vllt tust du dich damit leicher

Grüße

**Nur die Kenner können mit 10 Fingern bis 1023 zählen !!**
private int Main()
{
   string programmingSkills = getMySkills("programming")
   return = 1;
}
U
400 Beiträge seit 2008
vor 13 Jahren

Moin,

aus Erfahrung weiss ich, dass es oft nicht Synchron läuft, was bedeutet, dass du, wenn du im Server das erste mal auf empfangen gehst im Client schon alles gesendet sein kann und im server das dann eben alles als eine Übertragung gehandelt wird.

Ich vermute mal die latenz ist bei deinem Server-Client-Modell nicht optimal.

Du kannst es wie bei großen Serversystemen so machen, dass du vom Server eine Bestätigung schickst:

-> client sendet benutzernamen
-> client wartet auf erhalten
<- Server sendet erhalten
-> client sendet password
-> client wartetd auf erhalten
<- server sendet erhalten
usw.

Die Empfangsmethode unseres Gameservers (komplett in C#) sieht z.b. so aus

private void ReciveData(IAsyncResult result)
        {
            if (server.Running)
            {
                if (!Avilable)
                    Avilable = true;

                int offset = 0;
                try
                {
                  offset = cTCPConnection.Client.EndReceive(result);
                }
                catch(SocketException e)
                { 
                    this.Avilable = false;
                    return;
                }
                catch (ObjectDisposedException e)
                {
                    this.Avilable = false;
                    return;
                }
                
                if (offset == 0)
                {
                    Avilable = false;
                    return;
                }

                if (!Subscribed)
                {
                    subscribed = true;
                    server.CastConnectionEvent(this);
                }

                List<byte> bytes = new List<byte>();
                bytes.AddRange(buffer);
                bytes.RemoveRange(offset, bytes.Count - offset);

                server.CastTransmissionEvent(bytes.ToArray(), this);

                try
                {
                  cTCPConnection.Client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReciveData, null);
                }
                catch(SocketException e)
                { 
                    this.Avilable = false; 
                }
                catch (ObjectDisposedException e)
                {
                    this.Avilable = false;
                }
            }
        }

Das bedeutet er wartet auf ein asynchrones Ergebnis also eine Übertragung, macht diese dem Programmierer über ein Event zugänglich und geht dann wieder in den Empfangszustand über.

Das Gleiche mache ich auch beim Client

private void ReciveData(IAsyncResult result)
        {
            if (active)
            {
                int offset = 0;
                try
                {
                  offset = server.Client.EndReceive(result);
                }
                catch(SocketException e)
                { 
                    this.Active = false;
                }
                catch (ObjectDisposedException e)
                {
                    this.Active = false;
                }
                
                if (offset == 0)
                {
                    Active = false;
                    try
                    {
                      server.GetStream().Close();
                    }
                    catch(Exception e)
                    {}
                    
                    return;
                }

                List<byte> bytes = new List<byte>();
                bytes.AddRange(buffer);
                bytes.RemoveRange(offset, bytes.Count - offset);

                if (TransmissionReceipt != null)
                {
                    Connection c = new Connection(this.Server, null);
                    TransmissionReceipt.Invoke(bytes.ToArray(), c);
                }
            }
            if(server != null && !closed)
            try
            {
              server.Client.BeginReceive(buffer,0,buffer.Length,SocketFlags.None,ReciveData,null);
            }
            catch (SocketException e)
            {
                this.Active = false;
            }
            catch (ObjectDisposedException e)
            {
                this.Active = false;
            }
        }

Zudem verwalten wir die Verbindungen, die die Empfangsmethode haben als seperate Klassenobjekte einer Verbindungsklasse, sodass der Server dammit nichts mehr zu tun hat, ausser die Verbindung anzunehmen und wieder abzuwählen.

D
Derok Themenstarter:in
19 Beiträge seit 2011
vor 13 Jahren

danke für die schnellen antworten

@Smou, ob TcpClient oder Socket, kam beides das gleiche raus, wie oben schon beschrieben

Durch den Tip von Smou mit dem 8192 bin ich auf die idee gekommen das beide Send Methoden des cleints in eine Read methode des server landen. Habs überprüft und so wars auch

@Pria, werd nun erstmal nach ner eigenen Lösung weiter suchen, und wenn ich keine Lösung find deins Anschauen, bin einer der immer erst alles selber Lösen will, aber auf die idee das beide Strings die vom Client gesendet wurden in einer Read-methode des Servers landen, kam ich irgentwie nicht ^^

P
157 Beiträge seit 2010
vor 13 Jahren

ich würde vorschlagen, dass du am Anfang die Länge der zu sendenden Daten schickst.
Client --> Sende 4 Bytes (int) : Länge
Client --> Sende Name + char(29) + Passwort
Server --> Empfängt 4 Bytes (Länge)
Server --> empfängt 'Länge' Bytes

Jetzt muss der Server nur noch Name und Passwort trennen und fertig.

156 Beiträge seit 2010
vor 13 Jahren

bin im moment beim umsteigen von Java auf c# und hab mich dort schon öfters mit netzwerkprogrammierung beschäftigt, allerdings ist das in c# ein tick komplizierter ^^.

wenn du java.nio verwendest hast, dann kann ich es theoretisch nachvollziehen ... ansonsten ist es kein Unterschied zu C#


                byte[] bname = new byte[4096];
                work.meldung(false, "Warte auf name");
                //int bytesRead1 = clientStream.Read(bname, 0, 4096);
                clients.Receive(bname);
                work.meldung(false, "name empfangen");
                byte[] bpasswort = new byte[4096];
                work.meldung(false, "warte auf pw");
                //int bytesRead2 = clientStream.Read(bpasswort, 0, 4096);
                clients.Receive(bpasswort);
                work.meldung(false, "pw empfangen");

das ist Grundsätzlich nicht falsch - ist aber auch nicht richtig

Nun zur sache, das was auskommentiert habe bei dem server ist eine andere methode die ich mal in nen tutorial gesehen hab, dort bin ich aber bis zum gleichen punkt gekommen.
Verbindung baut es erfolgreich auf und der nick name wird übertragen.

da ist bestimmt alles auf einmal angekommen und das hast Du beim Einlesen des namens geschluckt ... anschließend wartest Du auf das Passwort - welches aber schon beim Namen mit gelesen wurde ... daher bleibt er hängen

daher ist folgender Vorschlag korrekt

ich würde vorschlagen, dass du am Anfang die Länge der zu sendenden Daten schickst.

nicht vorschlagen sondern machen ... es heißt nicht umsonst NetworkStream ... das ist ein Strom aus dem kontinuierlich Daten "fließen" ... gleiche wie beim Wasserhahn - um da einen Liter ab zumessen nimmt man auch einen Messbecher ... was eben die Längenangabe ist

D
Derok Themenstarter:in
19 Beiträge seit 2011
vor 13 Jahren

jo hab bei java mit java.io gearbeitet, war easy ^^ vom grundaufbau haste recht da ist es sehr gleich, nur das es bei einzelnen packeten verschicken easy war ^^

hab nun 2sachen erfolgreich gemacht,
der erste versuch war eigentlich die simplste, hab name und passwort mit ein Slash dazwischen zusammen gepackt und versendet dem entsprechend bei server seite dann halt wieder gesplittet. Probleme bei der möglichkeit, wenn es mal über die 4096bytes geht ^^. Erhöhen wär möglich aber effizient ist was anderes.

Die andere möglichkeit wie es ging war die von ppk auf die ich auch kam ^^, geht auch ohne probleme, ist halt nur die sache das man erstmal dem server sagen muss wielang das teil ist.

Ist jetzt Fraglich, hab vor, in ca 4monaten ein MMORPG an zu fangen, im moment bau ich mir das basis wissen in c# und der dafür vorgesehenen engine auf.

Was vermutet ihr wird von der latenzzeit schneller und effektiver sein? bzw wird sich dadurch überhaubt ein unterschied sichtbar machen? Und gibt es effektivere Methoden daten zu übertragen?

Und damit nicht so Flames entstehen wie... noch so einer der ein mmorpg programmiert, hab bereits eins zu testzwecken programmiert, damals in java halt.
Man konnt kämpfen, lvln, die mobs hatten neh mini-ki, sachen kaufen, chatten, und pvp aber alles halt im maßstab des tests, also nichts davon ausgereift 😉
Und weiß auch wie viel zeit sowas in Anspruch nimmt, damals bei java, zur testzwecken schon alleine 4Monate gebraucht

156 Beiträge seit 2010
vor 13 Jahren

wieviel Spieler erartest Du? ... ggf. solltest Du von TCP auf UDP wechseln -> TCP Verbindung max Anzahl

D
Derok Themenstarter:in
19 Beiträge seit 2011
vor 13 Jahren

erwarten und das system auf neh bestimmt anzahl zu bringen sit immer so neh sache 😉
hab dann vor das min 300leute aufeinmal auf dem server sein können.

Ich weiß net UDP ist halt so neh sache, bin dem gegenüber halt etwas abgeneigt, weil wenn packete bei einem mmorpg verloren gehen, könnt das fatal sein, zb jemand kauft etwas aber nichts passiert ^^

D
Derok Themenstarter:in
19 Beiträge seit 2011
vor 13 Jahren

Werde nun 2 Sachen machen was hoffentlich die Leistung stark erweitert.

1tens, mein Server-Programm erstmal auf 64 bit laufen lassen.

2tens mich mit dem GC beschäftigen. und mal einen starken blick auf den Server-GC werfen, der laut meinen infos extra für multithreading server entwickelt wurde. wobei ich auch gehört habe das der Server-GC kaum mehr leistung bringen soll.

3.170 Beiträge seit 2006
vor 13 Jahren

Hallo Derok,

da ist bestimmt alles auf einmal angekommen und das hast Du beim Einlesen des namens geschluckt ... anschließend wartest Du auf das Passwort - welches aber schon beim Namen mit gelesen wurde ... daher bleibt er hängen

Das sehe ich ganz genauso - hier dürfte das ursprüngliche Problem begraben liegen.

Und sowas solltest Du Dir abgewöhnen

                String ids = ""+idss;

dafür gibt's in C#

                String ids = idss.ToString();

Gruß, MarsStein

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

D
Derok Themenstarter:in
19 Beiträge seit 2011
vor 13 Jahren

@MarsStein

das problem lag nicht daran ^^ und wurde auch schon vor längeren behoben

U
400 Beiträge seit 2008
vor 13 Jahren

Ist jetzt Fraglich, hab vor, in ca 4monaten ein MMORPG an zu fangen, im moment bau ich mir das basis wissen in c# und der dafür vorgesehenen engine auf.

Hab dir dazu eine PM geschickt