Как я могу заставить свою службу прослушивателя TCP правильно завершить работу?

Я пишу службу Windows, которая запускает прослушиватель TCP. Основной код работает нормально, но у меня есть несколько проблем с механикой службы Windows.

Прямо сейчас, когда моя служба запускается, она создает поток и запускает прослушиватель TCP в потоке. Затем, когда служба останавливается, она завершает этот поток:

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

Запуск работает нормально. Однако если я остановлю службу, процесс службы не завершится. Что я делаю неправильно?

[Кстати, сейчас я делаю это на VS2010 Beta 2, если это имеет значение.]


person davidcl    schedule 20.12.2009    source источник


Ответы (1)


Вместо того, чтобы завершать поток с помощью Thread.Abort(), вы должны реализовать некоторый метод shutdown(), который корректно закрывает сокет.

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

Когда Shutdown() вызывает socketListen.Close(), а рабочий поток в настоящее время ожидает нового подключения, будет вызвано SocketExcpetion. Мы просто игнорируем это.
В вашем методе OnStop() вы сначала даете экземпляру pmtListener возможность корректно завершить работу, вызывая myListener.Shutdown() (который затем устанавливает флаг завершения работы и закрывает сокет), а затем ждете (до ) определенный промежуток времени (например, одну секунду). Если поток все еще жив, попробуйте его завершить.

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
person VolkerK    schedule 20.12.2009
comment
Хорошо, предположим, что у pmtListener есть метод shutdown(). Как бы это выглядело, чтобы назвать это? Нужно ли мне присоединяться к созданному потоку? - person davidcl; 20.12.2009
comment
Дайте мне секунду или две для примера... Я не разработчик VB ;-) Краткая версия: используйте не статический метод (общий саб?), а экземпляр pmtListener (затемните myListener как новый pmtListener() ?) и запустите поток в его методе Main() (new Thread(AddressOf myListener.Main)) - person VolkerK; 20.12.2009
comment
Пример работает (проверено только как консольное приложение, а не сервис). Socket.Accept() вызывает (неуправляемый) код winsock, на который не влияет исключение ThreadAbortException. Интересно, есть ли способ сообщить об этом. Примечание для себя: это заняло слишком много времени. практиковать больше vb.net. Это это точка сети. - person VolkerK; 20.12.2009
comment
Я бы по-прежнему делал это таким образом, но я думаю, что технически отказоустойчивость Join() и Abort() не нужна, поскольку поток является фоновым потоком, который должен автоматически завершаться, когда процесс завершается. - person Matt Davis; 20.12.2009
comment
Если это надежно для службы, то да, избавьтесь от вызова Abort(). Но я бы сохранил соединение (тайм-аут). Максимальная задержка в одну секунду не повредит, если это даст потоку возможность очиститься первым. - person VolkerK; 20.12.2009
comment
Всем спасибо, это очень полезно! Мой прослушиватель использует класс .net TCPListener вместо Socket.Accept() — решает ли это проблему с ThreadAbortException? Первоначальные тесты говорят, что нет. - person davidcl; 20.12.2009
comment
Затем используйте метод Stop() TCPListener, msdn .microsoft.com/en-us/library/ - person VolkerK; 21.12.2009
comment
Да, это то, что я делаю. Спасибо! - person davidcl; 21.12.2009