Entwickler-Ecke

Basistechnologien - Blockierten Thread Beenden


Flitzs - Mi 05.11.08 22:34
Titel: Blockierten Thread Beenden
Hey,
mein Problem:

Ich schreibe gerade eine Consolenanwendung, in der ich in einem, von mir gestartet Thread, auf eingehene TCP Clients warte (also der Thread ist blockiert).
Nur lässt sich dieser Thread nicht mehr beenden, Thread.Abort() funktioniert nicht (die ThreadAbortException wird nicht ausgelöst).
Wie beende ich den Thread dann?

mfg Flitzs


Kha - Mi 05.11.08 22:49

Wie sieht denn dein TCP-Code aus? Es sollte eigentlich möglich sein, diesen auch non-blocking zu schreiben.


Flitzs - Do 06.11.08 15:19

Hey,


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
  
static void MailServer()
        {
            try
            {
                TcpListener Server;
                   try
                       {
                           Server = new TcpListener(IPA, 25);
                        
                       }
                       catch
                       {
                          Console.WriteLine("Server kann nicht gestartet werden!");
                          return;
                       }
                Server.Start();
                while (true)
                {
                    Thread T = new Thread(new ParameterizedThreadStart(ClientTalk));                    
                    T.Start(Server.AcceptTcpClient());
                }
            }
            catch (ThreadAbortException)
            {
                return;
            }
            catch (Exception ex)
            {
                Console.WriteLine("Server wird wegen Fehler beended: " + ex.Message);
                return;
            }
            
        }

Die Funktion MailServer wird in einem extra Thread ausgeführt die für jeden neuen TCP Client einen neun Thread start, in dem dann mit den Client interagiert.

mfg Flitzs


Kha - Do 06.11.08 15:33

Das asynchrone Gegenstück ist TcpListener.BeginAcceptSocket. Einen eigenen Thread brauchst du dann wahrscheinlich gar nicht mehr.


Flitzs - Do 06.11.08 16:33

Hey, mein momentaner Code sieht so aus:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
static void MailServer()
        {
            try
            {
                TcpListener Server;
                try
                {
                    Server = new TcpListener(IPA, 25);

                }
                catch
                {
                    Console.WriteLine("Server kann nicht gestartet werden!");
                    return;
                }
                Server.Start();
                while (true)
                    Server.BeginAcceptTcpClient(new AsyncCallback(ClientTalk),Server);                
            }
            catch (ThreadAbortException)
            {
                return;
            }
            catch (Exception ex)
            {
                Console.WriteLine("Server wird wegen Fehler beended: " + ex.Message);
                return;
            }

        }     
         
        static void ClientTalk(IAsyncResult  ar)
        {               
            TcpListener listener = (TcpListener)ar.AsyncState;            
            TcpClient Client = listener.EndAcceptTcpClient(ar);


Doch funktioniert das natürlich nicht, da ich die Methode ja immer wieder aufrufe. Nun stellt sich mir die Frage, wie ich die Schleife ersetze. Ich könnte ein weiteres  Server.BeginAcceptTcpClient nach dem TcpClient Client = listener.EndAcceptTcpClient(ar); in der Funktion ClientTalk schreiben, aber irgendwie kommt mir das nicht so vor als wäre das die Beste Lösung oder?

mfg Flitzs


Kha - Do 06.11.08 17:04

user profile iconFlitzs hat folgendes geschrieben Zum zitierten Posting springen:
Ich könnte ein weiteres  Server.BeginAcceptTcpClient nach dem TcpClient Client = listener.EndAcceptTcpClient(ar); in der Funktion ClientTalk schreiben, aber irgendwie kommt mir das nicht so vor als wäre das die Beste Lösung oder?
Ich denke, das ist schon der richtige Weg. Du musst die Schleife in eine (Pseudo-)Rekursion umwandeln:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
void AcceptClients(TcpListener listener)
{
  listener.BeginAcceptTcpClient(ar => {
    var client = listener.EndAcceptTcpClient(ar);
    [...]// neuen Thread für client abspalten...
    // auf nächsten Client warten
    AcceptClients(listener);
  }, null);
}


Gom_Uni - Sa 06.12.08 20:04

Hallo leute,

mir gehts genauso, ich habe einen Blockierenden Thread welcher aufgrund der TCP Server Datenanbindung nicht mehr zu terminieren ist.

Gibt es irgendeine saubere Lösung dafür?
Problem tritt in Zeile 13 auf, weil die blokierende Funktion BLOCKIERT

C#-Quelltext
1:
 Socket sock = listen.AcceptSocket();                    


Ablauf:

Create Listening Thread
Listen
Accept
Create Next Listening Thread
...
...
while(Receiving data)

Dies wurde deshalb so implementiert weil ein Client über den gesamten Zeitraum hinweg immer mit dem Server verbunden bleibt. Dies ist nötig da der Server auch asynchrone meldungen an alle clients senden können muss.

Nachdem ich C# erst seit 4 Tagen in Verwendung habe kann es sein dass die klasse etwas zusammengeflickt aussieht, aber bis jetzt funzts


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
class Server
    {
        static int mListen = 0;
        static int mConnection = 0;
        static List<Thread> mCrtTreads = new List<Thread>();

        static void DoListen()
        {
            IPAddress[] IPs = Dns.GetHostAddresses("localhost");
            TcpListener listen = new TcpListener(IPs[0], 22025);
            listen.Start();
            
            Socket sock = listen.AcceptSocket();
            StartListen();

            long size = 0;
            List<byte> buf = new List<byte>();

            do
            {
                size = sock.Receive(buf.ToArray());
                if (size > 0)
                {

                }
                else
                {
                    sock.Disconnect(true);
                    sock.Close();
                }
            }
            while (size > 0);
            Console.WriteLine("Communication Thread terminated / client disconnected");
        }

        public static void StartListen()
        {
            Thread t = new Thread(new ThreadStart(DoListen));
            t.Name = "Listenthread " + (mListen++).ToString();
            Console.WriteLine("[started] " + t.Name.ToString());
            mCrtTreads.Add(t);
            t.Start();
        }

        public static void Kill()
        {
            Console.WriteLine("Killing Threads... " + mCrtTreads.Count.ToString());
            foreach (Thread i in mCrtTreads)
            {
                //if (i.ThreadState == ThreadState.WaitSleepJoin)
                    i.Interrupt();

                // Zweifelhafter versuch diesen Thread zu killen :-)

                Console.WriteLine("..." + i.Name.ToString());
                i.Abort();

            }
            mCrtTreads = null;
        }
    }


mfg george


Kha - Sa 06.12.08 20:20

:welcome:

Ich sehe leider keine andere Möglichkeit als meine etwas gewöhnungsbedürftige Rekursion von oben.
Falls du übrigens keine speziellen Socket-Features benötigst, würde ich unbedingt (Begin)AcceptTcpClient benutzen


Gom_Uni - Sa 06.12.08 20:36

hy danke für deine mühe,

habe endlich das richtige gefunden. ich habe ne schleife mit

C#-Quelltext
1:
 while(!listen.Pending) {Sleep(1000);}                    

am laufen. Damit funzt alles.

mfg & thx