我有一个名为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()函数。下面的事件是一个Raised事件,我用它来通过后台进程中调用的事件来更新文本框。
以下是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调用中添加一些内容吗?
任何见解都将不胜感激,如果我需要更多信息,请告诉我。
答案 0 :(得分:3)
我真的不喜欢MSDN的示例代码,因为它使用了一个让你觉得有必要的while循环;如果有必要,那么你实际上需要听另一个线程。幸运的是,您不需要使用BackgroundWorker
,并且您可以轻松地执行while循环之外的所有操作,而无需锁定UI线程。
首先,创建一个名为Shared
的{{1}}方法(与其他共享回调和方法一起使用),它将执行Accept(Socket)
。另一种方法更容易,因为它将在两个地方运行,您可能稍后发现需要捕获异常。主Socket.BeginAccept
中的某个位置,在绑定Form
并收听后,您将首次调用IPEndPoint
,并提供用于收听的套接字。
我还建议创建一个Accept
方法,其中套接字是Receive(Socket)
中获得的新连接客户端。这也将改善组织,因为代码不仅仅是AcceptCallback
,例如捕获异常。
在BeginReceive
方法中,在您执行AcceptCallback
后,您将再次致电Receive
。这将在Accept
和Accept
之间不断循环。为了打破这个循环以停止监听,只需调用AcceptCallback
方法,将套接字字段设置为Nothing,并捕获这些异常:
Socket.Close
(与关闭无关)SocketException
(因为关闭是一种处置)ObjectDisposedException
(偶尔的竞争条件会在无效(Nothing)检查通过的情况下发生,但在此之后,Socket将被处理并设置为null)在你的NullReferenceException
方法中,记得将Socket设置为Nothing(关闭套接字后),这将表示套接字已被丢弃。在运行任何方法之前,您还应检查监听器是否为Nothing(这将是您的StopServer
和Accept
方法),确保在偶尔的比赛中捕获AcceptCallback
条件。
这是关于NullReferenceExceptions
方法的基本模型。不过,这是与其余代码类似的模型。
Accept