代码之家  ›  专栏  ›  技术社区  ›  Ryan Pedersen

何时在套接字上调用BeginAccept

  •  0
  • Ryan Pedersen  · 技术社区  · 15 年前

    我在一个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,然后完成了对“客户机套接字”的工作。我不能把手指放在上面,但这味道不太好。用于侦听新连接的套接字是否应保留为字段?如果不是,你以后怎么叫关机?这是一个非常长的运行(周/月)过程。

    2 回复  |  直到 15 年前
        1
  •  0
  •   Steve Townsend    15 年前

    异步方式 Accept 工作是你通常发布的 BeginAccept 在接受一个连接后立即进行,这样您就可以尝试另一个传入连接了。我认为这里的流程非常典型——当您收到第一个传入连接的回调时,您会发出 EndAccept 完成然后再发布另一个 开始接受 让监听插座为下一个做好准备。

    你要用插座 s 对于第一个传入连接上的后续I/O,您确实需要保持这一点。这样做的逻辑是 clientSocket 使用 S 作为参数。

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

    这里有一个详细的描述如何完成这一切 here .

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

        2
  •  1
  •   jgauffin    15 年前

    发布的代码不会导致任何无法处理的异常,这些异常会关闭服务器。嗯,是的,但只是在听。

    另外,不要写 Throw ex ,它会破坏原始stacktrace。 Throw 就够了。

    至于endaccept/beginacept/handleevent,没有什么问题。