【问题标题】:VB.Net Properly shutdown an asynchronous socket server running on a background workerVB.Net 正确关闭在后台工作程序上运行的异步套接字服务器
【发布时间】:2012-03-21 05:39:45
【问题描述】:

我有一个名为 SocketSvr 的类,它处理异步套接字服务器。它是通过我的主窗体中的 BackgroundWorker 调用的。基本上,我只希望它在主窗体的文本框中显示我的套接字数据信息,并在主窗体上有一个服务器启动/停止按钮来执行这些操作。

这是来自 Form1.vb 的代码:

Public Class Form1

    Dim WithEvents Socketsvr As New SocketSvr

    Private Sub ToggleServerButton_Click(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles ToggleServerButton.Click

        If ToggleServerButton.Text = "Stop Server" Then
            ToggleServerButton.Text = "Start Server"
            Socketsvr.StopServer()
        Else
            ToggleServerButton.Text = "Stop Server"
            Socketsvr.StartServer()
        End If
    End Sub

    Private Sub UpdateOutput_Event(ByVal sender As Object, _
        ByVal text As String) Handles Socketsvr.UpdateOutput
        Me.ServerOutputTextbox.AppendText(text + vbCrLf)
    End Sub

End Class

上面很简单,基本上都是调用StartServer()或者StopServer()函数。下面的事件是一个引发事件,我使用它通过后台进程调用的事件来更新文本框。

以下是 Socketsvr.vb 的一些代码——我删除了不相关的代码以尽量减少帖子的废话。

Public Sub StopServer()
    bw.CancelAsync()
    allDone.Set()
End Sub 'StopServer

Public Sub StartServer()
    bw.WorkerSupportsCancellation = True
    bw.RunWorkerAsync()
End Sub 'StartServer

Private Sub bw_DoWork(ByVal sender As System.Object, _
    ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bw.DoWork
    ' Data buffer for incoming data.
    Dim bytes() As Byte = New [Byte](1023) {}

    ' Establish the local endpoint for the socket.
    Dim ipHostInfo As New IPHostEntry
    ipHostInfo.AddressList = New IPAddress() _
        {New IPAddress(New [Byte]() {127, 0, 0, 1})}
    Dim ipAddress As IPAddress = ipHostInfo.AddressList(0)
    Dim localEndPoint As New IPEndPoint(ipAddress, 8888)

    ' Create a TCP/IP socket.
    Dim listener As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)

    ' Bind the socket to the local endpoint and listen for incoming connections.
    listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1)
    listener.Bind(localEndPoint)
    listener.Listen(100)
    While True
        ' If cancellation is pending, shut down server
        If bw.CancellationPending Then
            Out("Server stopped at " + DateTime.Now.ToString())
            Exit While
        End If
        Out("Server started at " + DateTime.Now.ToString())
        ' Set the event to nonsignaled state.
        allDone.Reset()

        Try
            ' Start an asynchronous socket to listen for connections.
            listener.BeginAccept(New AsyncCallback(AddressOf AcceptCallback), listener)
        Catch ex As Exception
            MessageBox.Show(ex.ToString, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try

        ' Wait until a connection is made and processed before continuing.
        allDone.WaitOne()
    End While
End Sub

我不确定如何在 StopServer() 函数运行时正确关闭来自客户端的请求。在上面的代码中,我为后台进程放置了一个取消队列。有趣的是,一旦 StopServer() 运行,它会再接受一个连接,然后停止接受。

如果我从上面的代码中删除以下行:

listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1)

--当我第二次尝试启动服务器时它崩溃了(抱怨我不能重用套接字,显然)我的猜测是我需要在后台工作人员的 bw.CancellationPending 调用中添加一些东西?

任何见解都将不胜感激,如果我需要更多信息,请告诉我。

【问题讨论】:

    标签: vb.net sockets asynchronous backgroundworker


    【解决方案1】:

    我不太喜欢 MSDN 的示例代码,因为它使用了一个 while 循环,这让您认为这是必要的;如果有必要,那么您实际上需要在另一个线程上收听。幸运的是,您不需要使用 BackgroundWorker,而且您可以轻松地在 while 循环之外执行所有操作,而无需锁定 UI 线程。

    首先,创建一个名为Accept(Socket)Shared 方法(与其他共享回调和方法一起使用),它将执行Socket.BeginAccept。另一种方法更简单,因为它将在两个地方运行,并且您以后可能会发现需要捕获异常。在你的主Form 的某个地方,绑定IPEndPoint 并监听后,你将把你的第一个调用添加到Accept,提供用于监听的套接字。

    我还建议创建一个Receive(Socket) 方法,其中套接字是在AcceptCallback 中获得的新连接的客户端。这也将改善组织,因为除了BeginReceive 之外还有更多代码,例如捕获异常。

    在您的AcceptCallback 方法中,在您执行Receive 之后,您将再次调用Accept。这将在AcceptAcceptCallback 之间不断循环。为了打破这个循环停止监听,只需调用Socket.Close方法,将socket字段设置为Nothing,然后捕获这些异常:

    • SocketException(与 Close 无关)
    • ObjectDisposedException(因为关闭是一种处置)
    • NullReferenceException(偶尔会发生争用情况,空值(Nothing)检查将通过,但之后,Socket 将被释放并设置为空)

    在您的StopServer 方法中,请记住将 Socket 设置为 Nothing(在关闭套接字之后),这将表明套接字已被释放。在运行任何方法之前,您还应该检查侦听器是否为 Nothing(这将是您的 AcceptAcceptCallback 方法),确保捕获 NullReferenceExceptions 以应对偶尔的竞争条件。

    这是一个关于Accept 方法的基本模型。不过,这是与其余代码类似的模型。

    Private Shared Sub AcceptCallback(ar As IAsyncResult)
        Dim listener As Socket = DirectCast(ar.AsyncState, Socket)
    
        If listener Is Nothing Then Return
    
        Try
            Dim newClient As Socket = listener.EndAccept(ar)
    
            Receive(newClient)
        Catch ex1 As SocketException
            Return
        Catch ex2 As ObjectDisposedException
            Return
        Catch ex3 As NullReferenceException
            Return
        End Try
    
        Accept(listener)
    End Sub
    

    【讨论】:

    • 感谢您的支持——由于我对 VB.Net 非常缺乏经验,我仍在努力解决这个问题——很快就会回复您,看看一切如何。在此期间有一个 +1,一旦我让它工作就会标记为已回答:)
    • 我也想做同样的事情,基于微软异步服务器示例的用于套接字服务器的 wpf ui。我会很感激有人可以在 c# 中提供修改后的代码。
    • 因为我不能编辑我写的评论,这里是一个编辑过的副本。我也想做同样的事情,wpf ui for socket server based on microsoft async server sample。我会很感激有人可以在 c# 中提供修改后的代码。我有一些 c# 经验,但很少有网络编程经验。或者,如果它很麻烦,你能指出我推荐的 wpf 中的异步套接字服务器实现吗?谢谢。
    猜你喜欢
    • 1970-01-01
    • 2023-04-05
    • 2011-07-13
    • 2023-03-24
    • 1970-01-01
    • 2014-10-02
    • 1970-01-01
    • 2021-07-24
    • 2015-06-24
    相关资源
    最近更新 更多