views:

377

answers:

1

I'm writing a Windows service which starts a TCP listener. The core code works fine, but I'm having several problems with the mechanics of a Windows service.

Right now, when my service starts up, it creates a thread and starts the TCP listener in the thread. Then, when the service stops, it terminates that thread:

Public Class txnSocketService
    Inherits System.ServiceProcess.ServiceBase

    Private listenerThread As Thread


    Public Sub New()
        Me.ServiceName = "txnSocketService"
        Me.CanStop = True
        Me.CanPauseAndContinue = True
        Me.AutoLog = True
    End Sub

    Shared Sub Main()
        System.ServiceProcess.ServiceBase.Run(New txnSocketService)
    End Sub

    Protected Overrides Sub OnStart(ByVal args() As String)
        listenerThread = New Thread(AddressOf pmtListener.Main)
        listenerThread.IsBackground = True
        listenerThread.Start()
    End Sub

    Protected Overrides Sub OnStop()
        listenerThread.Abort()
    End Sub

    Private Sub InitializeComponent()
        '
        'txnSocketService
        '
        Me.ServiceName = "txnSocketService"

    End Sub
End Class

Starting up works fine. If I stop the service, however, the service process doesn't terminate. What am I doing wrong?

[By the way, I'm currently doing this on VS2010 Beta 2, if that matters.]

+5  A: 

Instead of terminating the thread with Thread.Abort() you should implement some shutdown() method that gracefully closes the socket.

e.g.

Public Class pmtListener
  Protected shutingdown As Boolean = False
  ' [...] '
  Public Sub Shutdown()
    shutingdown = True
    socketListen.Close()
  End Sub
  Sub Main()
    Dim socketAccepted As Socket
    shutingdown = False
    socketListen.Listen(3)
    While Not shutingdown
      Try
        socketAccepted = socketListen.Accept()
        ' do something with socketAccepted '
        socketAccepted.Close()
        socketAccepted = Nothing
      Catch ex As SocketException
        If shutingdown Then
          ' ignoring it '
        End If
      End Try

    End While
  End Sub

When Shutdown() calls socketListen.Close() and the worker thread is currently waiting for a new connection a SocketExcpetion will be raised. We just ignore that.
In your OnStop() method you first give the pmtListener instance a chance to shutdown gracefully by calling myListener.Shutdown() (which then sets the shutingdown flag and closes the socket) and then wait (up to) a certain timespan (e.g. one second). Should the thread still be alive try to terminate it.

Public Class txnSocketService
  Inherits System.ServiceProcess.ServiceBase

  Protected myListener as pmtListern
  Protected listenerThread As Thread

  ' [...] '
  Protected Overrides Sub OnStart(ByVal args() As String)
    myListener = new pmtListener
    listenerThread = New Thread(AddressOf myListener.Main)
    listenerThread.IsBackground = True
    listenerThread.Start()
  End Sub

  Protected Overrides Sub OnStop()
    myListener.Shutdown()
    listenerThread.Join(1000) ' give it a second '
    If listenerThread.IsAlive Then
      listenerThread.Abort()
    End If
  End Sub
VolkerK
Okay, let's say pmtListener has a shutdown() method. What would it look like to call it? Do I need to rejoin with the spawned thread?
davidcl
Give me a second or two for an example ...I'm not a VB developer ;-) Short version: Don't use a static method (shared sub?) but an instance of pmtListener (dim myListener as new pmtListener() ?) and start the thread on its Main() method (new Thread(AddressOf myListener.Main))
VolkerK
The example seem to work (only tested as a console app, not a service). Socket.Accept() calls (unmanaged) winsock code that isn't affected by the ThreadAbortException. I wonder if there's way to "make it aware". Note to self: That took too long. practice more vb.net. It _is_ dotnet.
VolkerK
I would still do it this way, but I think technically the Join() and the Abort() failsafe are not necessary since the thread is a background thread, which should automatically terminate when the process goes away.
Matt Davis
If that's reliable for a service then yes, get rid of the call to Abort(). But I'd keep the Join(timeout). A maximum delay of one second shouldn't hurt if it gives the thread a chance to clean up first.
VolkerK
Thanks all, this is very helpful! My Listener uses the .net class TCPListener instead of Socket.Accept()-- does this solve the issue with ThreadAbortException? Initial tests say no.
davidcl
Then use the TCPListener's Stop() method, http://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.stop.aspx
VolkerK
Yep, that's what I'm doing. Thanks!
davidcl