何时在套接字上调用BeginAccept

时间:2010-11-26 19:36:29

标签: c# .net vb.net sockets

我在Windows服务中遇到以下类,这些类遇到一些不同的奇怪关机行为。服务器不时关闭,事件日志中只显示此消息,跟踪日志中没有消息,“广播服务意外终止。已经完成了1次。”

Public Class ServerSocket
    Implements IServerSocket

    Public Event ClientConnected(ByVal sender As Object, ByVal e As EventArgs(Of IClientSocket)) Implements IServerSocket.ClientConnected

    Private _socket As Socket
    Private ReadOnly _settings As IBroadcasterServiceSettingsSection
    Private ReadOnly _traceSource As ITraceSource

    Public Sub New()
        Me.New(BroadcasterServiceSettingsSection.GetSection, BroadcasterTraceSource.Instance)
    End Sub

    Public Sub New(ByVal settings As IBroadcasterServiceSettingsSection, ByVal traceSource As ITraceSource)
        _settings = settings
        _traceSource = traceSource
    End Sub

    Public Sub Listen() Implements IServerSocket.Listen
        Dim endPoint As New IPEndPoint(System.Net.IPAddress.Parse(_settings.BroadcasterIPAddress), _settings.BroadcasterPortNumber)
        Try
            _socket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
            _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1)
            _socket.Bind(endPoint)
            _socket.Listen(SocketOptionName.MaxConnections)
            _socket.BeginAccept(New AsyncCallback(AddressOf AcceptCallback), Nothing)
            _traceSource.TraceInformation("ServerSocket listening for new clients.")
        Catch ex As Exception
            _traceSource.TraceCritical("ServerSocket caughtException trying to wait for a new client.")
            Throw ex
        End Try
    End Sub

    ''' <summary>
    ''' First attempts to shutdown the socket to clean up any remaining data left to send or receive. Then closes
    ''' the socket to release all connections and clean up unmanaged resources. See also <seealso cref="System.Net.Sockets.Socket.Shutdown">Socket.Shutdown</seealso>
    ''' and <seealso cref="System.Net.Sockets.Socket.Close">Socket.Close</seealso>
    ''' </summary>
    Public Sub Close() Implements IServerSocket.Close
        Try
            _socket.Shutdown(SocketShutdown.Both)
        Catch ex As Exception
            _traceSource.TraceEvent(TraceEventType.Error, "Shutting down Server Socket caused an exception.", ex.Message, ex.StackTrace)
        End Try

        Try
            _socket.Close()
        Catch ex As Exception
            _traceSource.TraceEvent(TraceEventType.Error, "Closing the Server Socket caused an exception.", ex.Message, ex.StackTrace)
        End Try

        _traceSource.TraceEvent(TraceEventType.Information, "ServerSocket closed.")
    End Sub

    Private Sub AcceptCallback(ByVal ar As IAsyncResult)
        Dim s As Socket = Nothing

        Try
            s = _socket.EndAccept(ar)
        Catch ex As Exception
            _traceSource.TraceInformation("ServerSocket caught exception trying to get new socket for client.", ex.Message, ex.StackTrace)
        End Try

        Try
            ' call the begin accept as soon as possible so that I can get the next incoming client 
            _socket.BeginAccept(New AsyncCallback(AddressOf AcceptCallback), Nothing)
        Catch ex As Exception
            _traceSource.TraceEvent(TraceEventType.Critical, "ServerSocket caughtException trying to wait for a new client.", ex.Message, ex.StackTrace)
        End Try

        Try
            If s IsNot Nothing Then
                Dim clientSocket As IClientSocket = New ClientSocket(s)
                OnClientConnected(New EventArgs(Of IClientSocket)(clientSocket))
            End If
        Catch ex As Exception
            _traceSource.TraceEvent(TraceEventType.Critical, "9/23 Review: " + ex.ToString())
        End Try
    End Sub

    Private Sub OnClientConnected(ByVal e As EventArgs(Of IClientSocket))
        RaiseEvent ClientConnected(Me, e)
    End Sub
End Class

有关这个类的一个特别之处在于,在调用_socket.EndAccept之后立即调用_socket.BeginAccept,然后完成“Client Socket”的工作。我不能把手指放在上面,但这听起来不对劲。用于侦听新连接的套接字是否应保留为字段?如果没有,你以后怎么称之为关机呢?这是一个非常长的(数周/月)过程。

2 个答案:

答案 0 :(得分:1)

发布的代码不会导致任何可能导致服务器关闭的未处理异常。嗯,确实如此,但仅限于听。

另外,不要写Throw ex,它会破坏原始的堆栈跟踪。 Throw就足够了。

对于EndAccept / BeginAccept / HandleEvent,没有任何问题。

答案 1 :(得分:0)

异步Accept的工作方式是您通常在接受一个连接后立即发出BeginAccept,因此您已准备好进行另一次传入连接尝试。我认为这里的流程非常典型 - 当您获得第一个传入连接的回调时,您发出EndAccept来完成它,然后发出另一个BeginAccept以保持侦听套接字为下一个做好准备。 / p>

您将在第一个传入连接上使用套接字s进行后续I / O,因此您需要保留它。这样做的逻辑是使用clientSocket作为参数设置s

_socket是您的代码用来侦听所有传入连接的那个。

详细描述了这一切应该如何完成here

我在这里看不到套接字处理逻辑的任何问题。我建议您将调试器附加到您的服务中,并尝试确定退出时的上下文。