Laden...

TCP - Anwendung beendet sich nach "Socket.Close();" ohne Exception.

Erstellt von schismatic vor 12 Jahren Letzter Beitrag vor 12 Jahren 2.309 Views
S
schismatic Themenstarter:in
4 Beiträge seit 2012
vor 12 Jahren
TCP - Anwendung beendet sich nach "Socket.Close();" ohne Exception.

Hallo zusammen,

ich ärgere mich seit geraum Zeit mit einem Fehler herum, den ich bisher nicht allein beheben konnte.

Hier einleitend ein Servercode-Ausschnitt:

    public void StopSocketListening()
    {
        if (_listener != null)
        {
            SocketThreads.ForEach(delegate(Thread _SocketThread)
            {
                _SocketThread.Abort();             
            });
            
            Sockets.ForEach(delegate(Socket _Socket)
            {                
                if (_Socket.Connected)
                {
                    Debug.Print("Remove Client {0}", _Socket.RemoteEndPoint);
                    _Socket.Shutdown(SocketShutdown.Both);
                    _Socket.Close(); //:: <-- Problemquelle :(      
                }
            });

            _listener.Close();
       } 
    }

_listener ist vom Typ Socket und nimmt Verbindungsanfragen entgegen.
Die Methode um Clientverbindungen aufzunehmen läuft in einem eigenen Thread.

Sobald ein Client sich mit dem Server verbindet, wird diese Verbindung vom Typ Socket in der Liste "Sockets" gespeichert und zusätzlich ein Thread gestartet um Daten vom Client zu empfangen (wird in der Liste "SocketThreads" gespeichert).

Sobald ich nun die Funktion "StopSocketListening()" aufrufe, verabschiedet sich der Server ohne jedwede Exception zu werfen.

Das auskommentieren von "_Socket.Close();" führt dazu, das ein Dauerspam von einem getrennten Client am Port anliegt.


Read-Event occurred from: xxx.xxx.xxx.xxx:1461 - 0 bytes
Read-Event occurred from: xxx.xxx.xxx.xxx:1461 - 0 bytes
Read-Event occurred from: xxx.xxx.xxx.xxx:1461 - 0 bytes
Read-Event occurred from: xxx.xxx.xxx.xxx:1461 - 0 bytes
Read-Event occurred from: xxx.xxx.xxx.xxx:1461 - 0 bytes
Read-Event occurred from: xxx.xxx.xxx.xxx:1461 - 0 bytes
Read-Event occurred from: xxx.xxx.xxx.xxx:1461 - 0 bytes
Read-Event occurred from: xxx.xxx.xxx.xxx:1461 - 0 bytes
Read-Event occurred from: xxx.xxx.xxx.xxx:1461 - 0 bytes

Bin für jeden Lösungsvorschlag dankbar 😃

B
387 Beiträge seit 2005
vor 12 Jahren

Hallo,

was mir hier nicht gefällt, ist das hier:

_SocketThread.Abort();

Kann es sein, dass du die Threads abschießt, wärend sie gerade über den Socket lauschen? Wenn ja, würds mich nicht wundern, dass es da auf der Treiber-Ebene oder irgendwo innerhalb der Listen-Methoden Probleme gibt. Macht man ja normalerweise nicht, solche Threads einfach mit Abort abzuschießen.

Gruß
Roland

L
667 Beiträge seit 2004
vor 12 Jahren

Hallo schismatic,

Ich würde dir auch erstmal empfehlen, den Socket-Thread jeweils geordnet zu beenden. Wahrscheinlich hast Du Deine Thread-Methode blockierend gebaut, weswegen Du den Thread nur noch über Abort() abschießen kannst. Hierfür gibt es bessere Lösungswege, die nicht blockieren. Wenn Du Deine Socket-Thread Methode mal postest, kann man Dir da sicher helfen.

Woher der Dauerspam kommen könnte sieht man in Deinem Codeausschnitt nicht. Das liegt aber sicher an Dir und nicht an der Socket-Klasse 😃

"It is not wise to be wise" - Sun Tzu

S
schismatic Themenstarter:in
4 Beiträge seit 2012
vor 12 Jahren

Danke für die Hinweise bisher.

Hier der Code der als Thread je Client läuft und gestartet wird, sobald sicht ein Client verbunden hat:


    static void ListenCallback(object handlerobject)
    {
        Socket handler = (Socket) handlerobject;
        StateObject state = new StateObject();

        while (true)
        {
            try
            {
                state = new StateObject();
                state.workSocket = handler;           
                handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                    new AsyncCallback(ReadCallback), state);
                //:: Wait until reading is done
                ReadCallbackDone.WaitOne();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());                
                //:: Thread beenden wenn Fehler aufgetreten ist
                break;
            }
        }

    }

Hier noch der Code in dem die Daten vom Client entgegen genommen werden:


public static void ReadCallback(IAsyncResult ar)
    {
        String content = String.Empty;

        //:: Start reading from client
        ReadCallbackDone.Reset();

        StateObject state = (StateObject)ar.AsyncState;
        Socket handler = state.workSocket;

        int bytesRead = handler.EndReceive(ar);

        Debug.Print("Read-Event occurred from: {0} - {1} bytes", handler.RemoteEndPoint, bytesRead);

        if (bytesRead > 0)
        {
            Debug.Print("Recieved {0} bytes", bytesRead);


            state.sb.Append(Encoding.ASCII.GetString(
                state.buffer, 0, bytesRead));

            content = state.sb.ToString();

            Debug.Print("Recieved {0} bytes, Content so far: {1}", bytesRead, content);

            if (content.IndexOf("\0") > -1)
            {
                Debug.Print("Received all data");
                Debug.Print("Read {0} bytes from socket {1}. \n Data : {2}",
                    content.Length, handler.RemoteEndPoint, content);
                
                // Echo the data back to the client.
                Send(handler, content);
                //:: Finished reading
                ReadCallbackDone.Set();
            }
            else
            {
                Debug.Print("Not all data received");
                // More data expected
                handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                    new AsyncCallback(ReadCallback), state);
            }
        }
        else
        {
            //throw new Exception(); //:: Bringt auch den Server zum Absturz
        }
    }

Bezüglich des beenden der Threads wollte ich mittels "throw new Exception();" eine Ausnahme auslösen und die Endlosschleife verlassen. Resultat davon ist auch das beenden der Anwendung 😦

1.552 Beiträge seit 2010
vor 12 Jahren

Hallo schismatic,

du kannst while(true) mit while(!AbortThread) ersetzen, wobei AbortThread eine Eigenschaft ist, dann kannst du von ausserhab AbortThread = true setzen und beim nächsten Schleifendurchlauf verlässt er die Schleife. Dies wäre eine Möglichkeit den Thread ohne Thread.Abort zu beenden.
Seit .NET 4 sind weitere Technologien genau für diesen Zweck konzipiert worden:
Cancellation Token, für Hintergründe liese bitte evtl .NET 4 Cancellation Framework.

Resultat davon ist auch das beenden der Anwendung 😦

Ungefangene Fehler kann man auf Application-Ebene Fangen. Stichwort DispatcherUnhandledException Eventfür WPF bzw ThreadException Event für Windows Forms

Gruß
Michael

Mein Blog
Meine WPF-Druckbibliothek: auf Wordpress, myCSharp

L
667 Beiträge seit 2004
vor 12 Jahren

Hallo schismatic,

dein "Dauerspam" tritt dann auf, wenn ein BeginReceive sofort zurück kommt ohne etwas lesen zu können (bytesread > 0 == false). Dabei handelt es sich nicht um einen Spam des Clients sondern Deine Debug-Ausgabe "Read event occurred..." wird einfach schon ausgegeben bevor überhaupt erfolgreich gelesen wurde. Wenn der Socket tot ist, ist es allerdings schwierig erfolgreich zu lesen.

Kommen diese "Spam"-Ausgaben schnell hintereinander oder mit einiger Wartezeit dazwischen ? Es wäre interessant zu wissen, ob der Socket den Receive-Versuch einfach sofort abbricht und sofort zurück kommt oder ob er jedesmal in einen Timeout läuft.

"It is not wise to be wise" - Sun Tzu

S
schismatic Themenstarter:in
4 Beiträge seit 2012
vor 12 Jahren

Hallo Lynix,

es kommt zu keinerlei Timeout und somit kommt die Ausgabe "Read Event occured ..." hunderte male pro Minute da es in der Schleife von "ListenCallback" aufgerufen wird.

Normalerweise wartet die Schleife solange, bis irgendetwas am Socket passiert durch "ReadCallbackDone.WaitOne();"

Aber sobald der Socket mit "_Socket.Shutdown(SocketShutdown.Both);" (in der Methode "StopSocketListening") beendet wird, setzt der Dauerspam ein.

Deswegen wollte ich folgenden Code verwenden um die Endlosschleife zu verlassen und den Thread (ListenCallback) zu beenden:


//ListenCallback:
while (true)
        {
            try
            {
                ...
            }
            catch (Exception e)
            {
                //:: Thread beenden wenn Fehler aufgetreten ist
                break;
            }
        }



//ReadCallback:
if (bytesRead > 0)
        {
            ...
        }
        else
        {
            throw new Exception(); //:: Bringt auch den Server zum Absturz
        }

Leider bringt das unweigerlich auch den Server sofort zum Absturz.

L
667 Beiträge seit 2004
vor 12 Jahren

Also throw new Exception ist Murks, wie ja bereits ausreichend in diesem Thread gesagt wurde. Wenn Du unbedingt ein AutoresetEvent nutzen willst, dann nimm die WaitOne() Überladung, die einen Timeout-Parameter akzeptiert um die Blockade nach einer geeigneten Zeit X abzubrechen. Das in Verbindung mit dem Vorschlag von xxMUROxx sollte schonmal dazu führen, dass Du die Schleife gezielt sauber beenden kannst.

"It is not wise to be wise" - Sun Tzu

S
schismatic Themenstarter:in
4 Beiträge seit 2012
vor 12 Jahren

Hallo hallo,

Fehler lag tatsächlich daran, das der Thread nicht richtig beendet wurde und somit ständig am eigentlich geschlossenen Port gelauscht wurde.

Lösung wie schon von euch Vorgeschlagen durch eine globale boolesche Variable umgesetzt die den Threads mitteilt, wann sie die Endlosschleife zu Verlassen haben.

Besten Danke für die kompetente Hilfe! 👍