| |
VB.NET - Ein- und UmsteigerThread per Button sicher schließen | | | Autor: Wavemark | Datum: 02.07.12 12:45 |
| Hallo,
ich habe momentan ein Problem mit dem ich nicht so recht weiterkomme.
Ein kleiner TCP-Server der logischerweise in einem Thread laufen muss.
Nun möchte ich diesen Thread mit einem Button sowohl starten als auch schließen
um beispielsweise den Port ändern zu können.
Problem ist das Schließen des Threads. Es gibt viele Code-Samples, allerdings
wird dort nur gestartet und das ist eben nicht mein Problem.
ABORT funktioniert nicht da damit der Thread lediglich angehalten wird.
Vielleicht kann mir da jemand helfen.
Vielen Dank im voraus | |
Re: Thread per Button sicher schließen | | | Autor: Preisser | Datum: 02.07.12 14:49 |
| Hallo,
mit Thread.Abort() wird eine ThreadAbortException ausgelöst, was meist (falls nicht gerade unmanaged Code ausgeführt wird) zum sofortigen Abbruch des Threads führt (also ein hartes/abruptes Ende, allerdings wird z.B. Code in Finally-Blöcken noch ausgeführt).
Wenn der Thread allerdings beispielsweise durch blockierende Methoden häufig im WaitSleepJoin-Zustand befindet, ist es besser, den Thread zu interrupten, wodurch eine ThreadInterruptedException ausgelöst wird, sobald der Thread das nächste Mal in den WaitSleepJoin-Zustand wechselt (dadurch ist es besser vorhersehbar, wo die Exception auftreten wird).
Anscheinend befindet sich ein Thread jedoch beim Aufruf von TcpListener.AcceptTcpClient() bzw. .AcceptSocket() nicht im WaitSleepJoin-, sondern im Running-Zustand (wohl wegen des Aufrufs von blockierenden Win-APIs, also unmanaged Code), wodurch die Methode mit .Interrupt() nicht funktioniert. Man kann aber die Stop()-Methode des TcpListeners aufrufen, wodurch dieser auch mit dem Abhören aufhört und in .AcceptTcpClient() eine SocketException geworfen wird.
Beitrag wurde zuletzt am 02.07.12 um 15:09:46 editiert. | |
Re: Thread per Button sicher schließen | | | Autor: Wavemark | Datum: 02.07.12 16:40 |
| Es ist genauso, wie im 3.Absatz geschildert, TcpListener.AcceptTcpClient().
Nur, auch TcpListener.Stop() ändert nichts an der Situation. Ist bereits eingebaut. Der Thread lässt sich schließen, aber AcceptTcpClient() läuft weiter. Auch nach Application.Exit. | |
Re: Thread per Button sicher schließen | | | Autor: Preisser | Datum: 02.07.12 16:55 |
| Hallo,
bei mir führt der Aufruf von TcpListener.Stop() dazu, dass TcpListener.AcceptTcpClient() im anderen Thread unmittelbar danach eine SocketException mit der Message "Ein Blockierungsvorgang wurde durch einen Aufruf von WSACancelBlockingCall unterbrochen" wirft und somit der Thread beendet werden kann. Evtl. noch offene TCP-Verbindungen (TcpClients) bleiben dabei bestehen.
Ich habe dazu mal folgenden Code verwendet:
Private tcpListenerThread As Thread
Private tcpListener As TcpListener
Private Sub StartTcpListener()
tcpListener = New TcpListener(IPAddress.Any, 12345)
tcpListener.Start()
tcpListenerThread = New Thread( _
Sub()
RunTcpListenerThreadThread(tcpListener)
End Sub)
tcpListenerThread.Start()
End Sub
Private Sub RunTcpListenerThreadThread(listener As TcpListener)
Try
Do While True
Dim tcp As TcpClient = listener.AcceptTcpClient()
Debug.WriteLine("Accepted")
' den tcp-client verarbeiten....
'tcp.Close()
Loop
Catch ex As SocketException
'Vermutlich wurde .Stop aufgerufen
Debug.WriteLine(ex.ToString())
End Try
End Sub
Private Sub StopTcpListener()
tcpListener.Stop() 'Listening beenden
tcpListenerThread.Join() 'Auf Threadende warten
End Sub Dann kann beispielsweise ein Button StartTcpListener() aufrufen, womit der TcpListener gestartet wird und auf eingehende Verbindungen wartet. Ein anderer Button kann später StopTcpListener() aufrufen, womit der TcpListener beendet wird.
Beitrag wurde zuletzt am 02.07.12 um 16:58:32 editiert. | |
Re: Thread per Button sicher schließen | | | Autor: Wavemark | Datum: 02.07.12 18:04 |
| Bei mir kommt genau die gleiche Exception Nach STOP. Ich denke, es liegt gar nicht am Thread. der ist schon geschlossen. Wenn ich mit VS unterbreche, hängt er an dieser Zeile fest:
client = server.AcceptTcpClient
Es besteht keine Verbindung und es liegt auch keine an. | |
Re: Thread per Button sicher schließen | | | Autor: Preisser | Datum: 02.07.12 18:08 |
| Hallo,
ich fürchte ich verstehe dich nicht ganz. Wenn man Stop() aufruft, müsste die Methode AcceptTcpClient() ja die SocketException werfen, die man dann auffangen kann und wodurch sich der Thread beenden kann.
Wie genau sieht denn dein Code für den separaten Thread aus?
Beitrag wurde zuletzt am 02.07.12 um 18:11:42 editiert. | |
Re: Thread per Button sicher schließen | | | Autor: Wavemark | Datum: 02.07.12 18:36 |
| Das ist der Thread. Es gibt noch einen Zweiten am Ende (th), aber der wird nur bei Verbindung gestartet.
Sub ServerStart()
Log("Server START")
server = New TcpListener(ipendpoint)
server.Start()
While True ' wir warten auf eine neue verbindung...
Try
Try
client = server.AcceptTcpClient '<----- hier bleibt er
' hängen
Catch ex As Exception ' auch nach
' Application:Exit
MsgBox(ex.Message)
End Try
Dim c As New Connection
c.stream = client.GetStream
c.streamr = New StreamReader(c.stream)
c.streamw = New StreamWriter(c.stream)
Dim data() As String = (c.streamr.ReadLine).Split(Chr(126))
c.cmd = data(0)
c.nick = data(1)
c.pass = data(2)
list.Add(c)
Log(c.nick + " LogIn")
th.Start(c)
Catch ex As Exception
Log("Server STOP")
Exit While
End Try
End While
End Sub | |
Re: Thread per Button sicher schließen | | | Autor: Preisser | Datum: 02.07.12 19:06 |
| Hallo,
der innere Try-Catch-Block führt dazu, dass zwar die SocketException aufgefangen wird, die bei .AcceptTcpClient() auftreten kann, danach wird die Methode aber weiter ausgeführt und es könnten andere, unerwartete Exceptions auftreten (evtl. noch ein Exit While im Catch-BLock hinzufügen, oder z.B. einen Try-Catch-Block außerhalb der Schleife verwenden)
Soweit ich sehe, liest du bei einer eingehenden Verbindung auch im gleichen Thread schon etwas, bevor ein weiterer Thread für diesen TcpClient gestartet wird - dadurch kann es allerdings passieren, dass sich der Thread nicht beendet bzw. vorübergehend keine weiteren, in der Zwischenzeit eingehenden, TCP-Verbindungen annimmt, wenn der TcpListener gerade einen neuen TcpClient angenommen hat und dort die blockierende Read-Methode aufruft, der Client jedoch nichts sendet. Ich würde alle Lesevorgänge in dem neuen Thread durchführen.
(Da du den TcpListener erst im neuen Thread erstellst, könnte es passieren, dass dessen Referenz noch gar nicht im Feld (Objektvariable) "server" abgelegt wurde, wenn der andere (GUI-)Thread dessen Stop()-Methode aufrufen will - sollte aber mit dem aktuellen Problem nichts zu tun haben.)
Du schriebst im vorherigen Beitrag "Bei mir kommt genau die gleiche Exception Nach STOP".
In welcher Zeile tritt die Exception auf?
Beitrag wurde zuletzt am 02.07.12 um 19:18:35 editiert. | |
Re: Thread per Button sicher schließen | | | Autor: Wavemark | Datum: 02.07.12 19:39 |
| Diese Exception tritt nach Server.Stop im TryCatch Block für 'client = server.AcceptTcpClient' auf.
Das ist ja auch so ok, denk ich mal. Merkwürdig ist nur, dass AcceptTcpClient dadurch nicht beendet wird. Ich hab's hundert mal probiert. Ob ich nun per Button den Thread und Server abschalte oder ich mit Application.Exit die Anwendung schließe, AcceptTcpClient wird nicht verlassen. Ich verzweifle hier schon.
Was das Lesen im ersten Thread betrifft, ist das schon richtig. Dieses Programm ist auch nur ein Test und funktioniert mit einem Client wunderbar. Ich werde sicher noch Änderungen vornehmen. Mir ging es nur erst mal um die Funktion.
Übrigens vielen Dank, dass du dir soviel Zeit dafür nimmst | |
Re: Thread per Button sicher schließen | | | Autor: Wavemark | Datum: 03.07.12 01:34 |
| Ich hab das Problemchen gelöst. Oft sieht man wirklich den Wald vor Bäumen nicht.
Try
client = server.AcceptTcpClient
Catch ex As Exception
If ServerThread.ThreadState=ThreadState.Stopped Then
Log("Server STOP")
Exit While
End If
End Try | |
Re: Thread per Button sicher schließen | | | Autor: Samael | Datum: 05.07.12 20:48 |
| Und Deine Lösung ist eine unschöne Lösung. Hier die korrekte Lösung des Problems.
Du legst Dir ein Flag an, z. B. serverRunning. Bevor der Server gestartet wird:
z. B. so:
serverRunning = true
do
if not serverRunning then exit do
client = server.AcceptCilent()
loop So, klickst Du nun auf Stop, dann setzt Du das Flag auf false und verbindest Dich selbst auf Deinen Server, verstehst Du? Dein "temporärer" Client wird akzeptiert und bevor die .Accept-Funktion wieder aufgerufen wird der Thread beendet. Saubere Lösung, ohne Exeption und Try-Catch..
o/ | |
Re: Thread per Button sicher schließen | | | Autor: Preisser | Datum: 05.07.12 23:09 |
| Hallo,
diese Art des Thread-Beendens benutzt man, wenn der Thread dauerhaft etwas ausführt (z.B. Endlosschleife), um ihn auf saubere Weise zu beenden (bei Thread.Abort() kann man nicht vorhersehen, wo der Thread abgebrochen werden wird).
Wenn man aber blockierende Methoden benutzt, geht das nicht, weil der Thread ja auf eine Nachricht eines anderen Threads wartet - in dem Fall kann man Thread.Interrupt() benutzen, wodurch eine ThreadInterruptedException ausgelöst wird, wenn der Thread das nächste Mal im WaitSleepJoin-Zustand ist.
Da allerdings der Thread beim Aufruf von TcpServer.AcceptTcpClient() weiterhin im Running-Zustand ist (durch den Aufruf von blockierenden WinAPIs), geht dies auch nicht - man muss deshalb die Close()-Methode des TcpListeners aufrufen, damit in der blockierenden .AcceptTcpClient()-Methode eine SocketException geworfen wird und diese somit zurückkehrt.
Bei deinem Beispiel müsste sich zuerst ein weiterer Client zum Server verbinden, damit die Methode AcceptTcpClient() zurückkehrt und den Zustand des Flags prüfen kann - daher kann man hiervon nicht unbedingt als saubere Lösung sprechen.
In meinem vorherigen Beitrag hatte ich einen Beispielcode mit dem Aufruf der Close-Methode angegeben. Der Try-Catch-Block ist notwendig, denn irgendwie muss die AcceptTcpClient()-Methode ja mitteilen können, dass kein neuer Client angelegt wurde, sondern der TcpListener beendet wurde.
Beitrag wurde zuletzt am 05.07.12 um 23:37:08 editiert. | |
| Sie sind nicht angemeldet! Um auf diesen Beitrag zu antworten oder neue Beiträge schreiben zu können, müssen Sie sich zunächst anmelden.
Einloggen | Neu registrieren |
|
|
sevISDN 1.0
Überwachung aller eingehender Anrufe!
Die DLL erkennt alle über die CAPI-Schnittstelle eingehenden Anrufe und teilt Ihnen sogar mit, aus welchem Ortsbereich der Anruf stammt. Weitere Highlights: Online-Rufident, Erkennung der Anrufbehandlung u.v.m. Weitere InfosTipp des Monats sevAniGif (VB/VBA)
Anzeigen von animierten GIF-Dateien
Ab sofort lassen sich auch unter VB6 und VBA (Access ab Version 2000) animierte GIF-Grafiken anzeigen und abspielen, die entweder lokal auf dem System oder auf einem Webserver gespeichert sind. Weitere Infos
|