Laden...

Erkennen von Client-Disconnect bei Socket-Verbindungen

Erstellt von mindthegap vor 7 Jahren Letzter Beitrag vor 7 Jahren 2.842 Views
M
mindthegap Themenstarter:in
3 Beiträge seit 2016
vor 7 Jahren
Erkennen von Client-Disconnect bei Socket-Verbindungen

hallo,

ich bin recht neu bei WPF und c# und versuche gerade eine permante verbindung mit einem tcpip client aufzubauen.
klappt erstmal wenn beide partner verbunden sind.

wenn der client die verbindung beendet, würde ich dies gerne abfangen und den server wieder auf listen setzen. wäre nett wenn mir jemand den richtigen weg zeigt.

grüße


    private void initTcpMaster()
        {
            IPAddress ipMaster = anyIP.IsChecked == true ? IPAddress.Any : IPAddress.Parse(txtIPMaster.Text);
            int portMaster = Int32.Parse(txtPortMaster.Text);         
            mainThread = new Thread(new ParameterizedThreadStart(mainListener));
           
            object args = new object[2] { ipMaster, portMaster };
            mainThread.Start(args);
        }


        private void mainListener(object data)
        {

            Array argArray = new object[2];
            argArray = (Array)data;
            IPAddress ip = (IPAddress)argArray.GetValue(0);
            int port = (int)argArray.GetValue(1);

            TcpListener tcplistenerM = new TcpListener(ip, port);
            updateUi("listening");

            try
            {

                tcplistenerM.Start();

                while (!tcplistenerM.Pending())
                {
                    Thread.Sleep(sleepTimeM);
                }

                while (true)
                {

                    TcpClient clientM = tcplistenerM.AcceptTcpClient();
                    updateUi("connected");

                    // for multiple clients use thread
                    Thread tcpHandlerThreadM = new Thread(new ParameterizedThreadStart(tcphandlerM));
                    tcpHandlerThreadM.Start(clientM);
                }
            }
            catch (Exception ex)
            {
                throw new Exception("Fehler bei Verbindungserkennung", ex);
            }
        }


        private void tcphandlerM(object client)
        {
            try
            {

                if (tcpClientM == null)
                {
                    tcpClientM = (TcpClient)client;        // create client
                    so = tcpClientM.Client;                  // create a socket so as to get the ipaddress of the client
                    streamM = tcpClientM.GetStream();
                }

                // read data from the port
                byte[] message = new byte[100];

                streamM.Read(message, 0, message.Length);
                receiving_dataM = Encoding.ASCII.ReadCString(message);
 
                //get ipaddress of the client connected
                ipaddress_clientM = (IPAddress.Parse(((IPEndPoint)so.RemoteEndPoint).Address.ToString())).ToString();

                if (receiving_dataM == "")
                {

                    // ????  kommt "" wenn client verbindung beendet.
                    updateUi("closed by client");
                   
                } else
                {
            
                // show the string on ui -> ip addres+ data
                updateUi(ipaddress_clientM + " : " + receiving_dataM);

                //send back, für testzwecke
                // sending_data = receiving_data;
                ASCIIEncoding asen = new ASCIIEncoding();
                cM = asen.GetBytes(receiving_dataM);
                streamM.Write(cM, 0, cM.Length);
 
                // neuer thread
                Thread tcpHandlerThreadM = new Thread(new ParameterizedThreadStart(tcphandlerM));
                tcpHandlerThreadM.Start(client);
                }

            }
            catch (Exception e)
            {
                updateException(e.StackTrace);
            }

        }


        private void closeMainThread() { 
            // verbindungen schliessen
            if (mainThread != null)
            {
                
                streamM.Flush();
                streamM.Close();
                tcpClientM.Close();
                lblStatusM.Content = "disconnected";
                mainThread = null;
            }

        }


        //
        private void updateUi(string s)
        {
            Func<int> del = delegate ()
            {
                txtMessageInMaster.AppendText(s + System.Environment.NewLine);
                lblStatusM.Content =s;
                lblErrorM.Content = "";
                txtMessageInMaster.ScrollToEnd();

                if (s == "closed by client")
                {
                    
                    // ?????  muss man hier etwas beenden, wenn dann der client wieder verbindet, kommt auch wieder ein leeres Bytearray, 

                }
            
                return 0;
            };
            Dispatcher.Invoke(del);

        }

        //for showing if any error occurred
        private void updateException(string s)
        {
            Func<int> del = delegate ()
            {
                lblStatusM.Content = "";
                lblErrorM.Content = "Error : " + s;
                return 0;
            };
            Dispatcher.Invoke(del);

        }
16.807 Beiträge seit 2008
vor 7 Jahren
  1. nimm Tasks statt Threads. Der Umgang mit Tasks ist einfacher
  2. Finger weg von untypisierten Listen wie Array. Verwende List<T> oder zumindest Array<T>
  3. Mische keine Programmlogik mit UI ( [Artikel] Drei-Schichten-Architektur )
  4. Streams sollte man disposen. Siehe dazu Dispose() und using()

Sockets können nicht erkennen, ob der Client noch existiert bzw. "verbunden ist".
Es geht nur via Polling.

Ich hab dazu folgendes Snippet. Kenne die ursprüngliche Quelle nicht mehr.

public static bool IsConnected(this TcpClient tcpClient)
{
   if ( tcpClient.Client.Poll( 0, SelectMode.SelectRead ) )
   {
      byte[] buff = new byte[1];

      return ( tcpClient.Client.Receive(buff, SocketFlags.Peek ) > 0)
   }
}

Aber warum schlägst Du Dich bei einem simplen Chat überhaupt mit Sockets rum?
Nimm doch dazu zB. SignalR, da kriegst Du sowas geschenkt.

M
mindthegap Themenstarter:in
3 Beiträge seit 2016
vor 7 Jahren

danke, schaue mir direkt SignalR an. und die List<T> beherzige ich gerne...

C
2.121 Beiträge seit 2010
vor 7 Jahren

Ein Server ist normalerweise immer im Listenmodus, weil er nicht nur auf einen einzigen Client hören will. Natürlich kann er das handhaben wie er möchte, aber falls du wirklich nur einen einzigen Client haben willst würde ich auch immer "hören" und nur den einen Client akzeptieren. Sprich der muss sich anmelden und dann schließt du die Verbindung wieder wenn es nicht dein Wunsch Client ist.

Meines Wissens gibt es eine Exception wenn ein Client sich regulär beendet. Müsstest du ausprobieren.

M
mindthegap Themenstarter:in
3 Beiträge seit 2016
vor 7 Jahren

erst einmal danke für die hilfreichen Antworten!

kurz zur Erklärung: was ich versuche in wpf zu bauen ist eine Bridge für alle möglichen Protokolle zur Verwendung in Medieninstallationen, z.B. arduino seriell, via UDP an Android Tablets, oder Android an DMX... vvvv, openframework, flash...

Bisher basiert dies überwiegend auf einem Schwung python Skripte. Schön wäre es hier eine bessere visuelle Kontrolle zu bekommen, ok, das ginge auch in python, aber parallel kann ich mir wpf etwas aneignen.

Das mit den Layern ist mir klar sein, ist aber für den ersten Test eines Protokolls nicht so wichtig, wenn klar ist wie es funzt werde ich das in Klassen zerlegen.

grüße

16.807 Beiträge seit 2008
vor 7 Jahren

Dann hab ich das mit dem Chat falsch verstanden; dann ist SignalR hier eher nicht geeignet.

W
872 Beiträge seit 2005
vor 7 Jahren

Die saubere Lösung ist es immer eine Ping Nachricht in das Protokoll einzubauen, die der Server alle x Sekunden schickt und die der Client dann in y Sekunde beantworten muss - Firewalls kappen gerne Verbindungen bei Inaktivität und hängende Clients möchte man auch lieber abhängen.
Exceptions gibt es nur, wenn der Client sich unsauber beendet - beim Ziehen eines Netzwerkkabels zum Beispiel sieht man nix.

3.003 Beiträge seit 2006
vor 7 Jahren

Die saubere Lösung ist es immer eine Ping Nachricht in das Protokoll einzubauen, die der Server alle x Sekunden schickt und die der Client dann in y Sekunde beantworten muss

Genau! Siehe auch RFC 1459, Absatz 4.6.2. - das sind Spezifikationen, an die man sich durchaus halten kann, auch wenn man kein IRC implementiert (die Jungs haben sich dabei ja was gedacht, und IRC gibt's nun auch schon ein, zwei Tage).

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)