C# Socket connection Datenübertragung

Rooky420

Cadet 4th Year
Registriert
Nov. 2015
Beiträge
98
Hallo Community,

ich habe gerade ein kleines Mühlespiel fertiggestellt und beim Multiplayer im lokalen netzwerk ist ein fehler aufgetreten bei dem ich nicht weiß wie ich ihn behandeln soll.

Ich habe im Spiel verschiedene Befehle die ich senden kann in einem "Command" enum.
Allgemeiner Stringaufbau:
Command, String[] parameter
nach jedem parameter und nach dem Kommando ist immer eine Pipe "|" um die einzelnen strings klar zu trennen
Also beispielsweise:
"SetStone|true|In|5"
(SetStone ist das kommando was getan werden soll | true (farbe ist schwarz bei false farbe ist weiß | In (Innerer ring) | 5 (Position auf dem ring))

Nun das Problem:
Ich habe folgenden String empfangen:
"SetStone|true|Out|6NextPlayer"
Dies hat natürlich eine Exception außgelößt da man den letzten parameter nicht in einen integer umwandeln kann.
"NextPlayer" ist das Kommando das Übertragen wird wenn ein Spieler seinen Zug beendet hat.

Es ist also passiert dass 2 Kommandos gesendet wurden aber diese 2 Kommandos als 1 Kommando von meinem Spiel empfangen wurde. Wie kann das Passieren bzw. wie kann ich es lößen dass dieses verhalten nicht mehr vorkommen kann?
Meistens funktioniert es ja einwandfrei jedoch kann es passieren.
Hier der Quellcode meiner Netzwerkklassen:
ConnectionClient und ConnectionHost erben von ConnectionBase.

Code:
 public delegate void ConnectedDelegate(bool Connected);
    public delegate void DataReceived(String data);

    public abstract class ConnectionBase
    {
        protected Socket _clientSocket;
        protected int _port;
        protected byte[] _buffer = new byte[1024];
        public virtual event ConnectedDelegate Connected;
        public virtual event DataReceived DataReceived;

        public ConnectionBase()
        {
            AppDomain.CurrentDomain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit);
        }

        protected void CurrentDomain_ProcessExit(object sender, EventArgs e)
        {
            try
            {
                Shutdown();
            }
            catch { }
        }

        public void Shutdown()
        {
            try
            {
                if (_clientSocket != null)
                {
                    String shutdown = "Shutdown";
                    byte[] bdata = GetBytes(shutdown);

                    _clientSocket.Send(bdata);
                }
                //shutdown Fehler
                ShutSocketsDown();
            }
            catch (Exception ex){ }
        }

        protected byte[] GetBytes(string str)
        {
            byte[] bytes = new byte[str.Length * sizeof(char)];
            System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
            return bytes;
        }

        protected string GetString(byte[] bytes)
        {
            char[] chars = new char[bytes.Length / sizeof(char)];
            System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
            return new string(chars);
        }

        protected void ReceiveCallback(IAsyncResult AR)
        {
            if(Thread.CurrentThread.Name == null)
                Thread.CurrentThread.Name = "Connection Receive Callback Thread";

            Socket socket = (Socket)AR.AsyncState;

            int received = socket.EndReceive(AR);
            byte[] dataBuf = new byte[received];
            Array.Copy(_buffer, dataBuf, received);
            String text = GetString(dataBuf);
               
            if (text.Equals("Shutdown"))
            {
                ShutSocketsDown();
                return;
            }

            if (DataReceived != null)
                DataReceived(text);
       
            socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), socket);
        }

        public void Send(String data)
        {
            byte[] bdata = GetBytes(data);
            _clientSocket.BeginSend(bdata, 0, bdata.Length, SocketFlags.None, new AsyncCallback(SendCallback), _clientSocket);
        }

        protected void SendCallback(IAsyncResult AR)
        {
            Socket socket = (Socket)AR.AsyncState;
            socket.EndSend(AR);
        }

        protected virtual void ShutSocketsDown()
        {
            if (_clientSocket != null)
            {
                ShutdownSocket(_clientSocket);
                try { 
                //_clientSocket.Dispose();
                }
                catch (Exception ex){ }
                _clientSocket = null;
            }
        }

        protected void ShutdownSocket(Socket socket)
        {
            byte[] buffer = new byte[1024];
            if (socket.Connected)
            {
                socket.Shutdown(SocketShutdown.Send);
                try
                {
                    int read = 0;
                    while ((read = socket.Receive(buffer)) > 0)
                    { }
                }
                catch
                {
                    //ignore
                }
            }
            socket.Close();
        }
    }

Code:
 public class ConnectionHost : ConnectionBase
    {
        private Socket _serverSocket;
        public override event ConnectedDelegate Connected;

        public ConnectionHost(int Port) : base()
        {
            this._port = Port;
        }

        public void StartServer()
        {
            _serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            _serverSocket.Bind(new IPEndPoint(IPAddress.Any, _port));
            _serverSocket.Listen(10);
            _serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null);
        }

        protected void AcceptCallback(IAsyncResult AR)
        {
            Socket socket = _serverSocket.EndAccept(AR);
            _clientSocket = socket;
            Connected(true);
            socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), socket);
        }

        protected override void ShutSocketsDown() 
        {
            base.ShutSocketsDown();
            if (_serverSocket != null)
            {
                ShutdownSocket(_serverSocket);
                try
                {
                    //_serverSocket.Dispose();
                }
                catch (Exception ex) { }
                //_serverSocket = null;
            }
        }
    }

Code:
public class ConnectionClient : ConnectionBase
    {
        private IPAddress _ipAdress;
        public override event ConnectedDelegate Connected;

        public ConnectionClient(String IPorName, int Port) : base()
        {
            this._port = Port;
            bool isIP = IPAddress.TryParse(IPorName.Trim(), out _ipAdress);
            if(!isIP)
                _ipAdress = Dns.GetHostEntry(IPorName).AddressList[0];
        }

        public bool Connect()
        {
            _clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //bool connect = ConnectAsync(_ipAdress).Result;
            bool connect = ConnectSocket(_ipAdress);
            Connected(connect);
            if(connect)
                _clientSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), _clientSocket);
            return connect;
        }

        private async Task<bool> ConnectAsync(IPAddress ipserver)
        {
            return await Task.Factory.StartNew(() => LoopConnect(ipserver));
        }

        private bool ConnectSocket(IPAddress ipserver)
        {
            return LoopConnect(ipserver);
        }

        private bool LoopConnect(IPAddress ipserver)
        {
            int attempts = 0;
            while (!_clientSocket.Connected)
            {
                try
                {
                    attempts++;
                    _clientSocket.Connect(ipserver, _port);
                    return true;
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
                if (attempts >= 10 && !_clientSocket.Connected)
                    return false;
            }
            return false;
        }
    }
 
In C# kannst du für sowas wunderbar den TcpClient benutzen und nen StreamReader und Writer an den Stream hängen.
Um zu senden reicht dann sw.WriteLine(meintext); und empfangen meintext = sr.ReadLine();

Ich hab mir deinen Code noch nicht genau angeguckt, aber es scheint das du kein "Trennzwichen" von Befehlen hast. Bei Read/Write Line ist zum Beispiel der Zeilenumbruch das Ende eines Befehls

Ich hab auch bisher immer den faulen weg mit StreamWriter/Reader genutzt und mich mit den Bytearrays noch nie wirklich viel beschäftigt. Kann also auch sein das man kein Trennzeichen braucht. Da fehlt mir die Erfahrung/Wissen.

Edit: Das BeginSend lässt doch darauf schließen das du auch wieder aufhören musst oder?
 
Zuletzt bearbeitet:
1. Wie Frazer1 schon sagte, nutze am besten TCP-Client
2. Nicht mehr Begin/End nutzen, sondern Async
3. Brauchst du etwas um entweder festzulegen, wann ein neues Kommando beginnt (z.B. Newline oder einfach ; ) oder aber du sendest zu beginn wie lang eine Command ist und parst dann nur so viele Bytes, alles danach ist wieder ein neuer Command.

Edit: Noch ein Tipp: setze "NoDelay" auf true.
 
Zuletzt bearbeitet:
Danke für die antworten.
Ich habe nun eine Sendequeue eingebaut damit immer nur maximal 1 kommando gleichzeitig gesendet werden kann. ich hoffe es lößt das Problem.
 
Damit umgehst eventuell du das Symptom, behebst aber nicht die Ursache. Das ist der falsche Weg...
 
Zurück
Oben