C# 何时在套接字上调用BeginAccept

C# 何时在套接字上调用BeginAccept,c#,.net,vb.net,sockets,C#,.net,Vb.net,Sockets,我有一个Windows服务中的类,它正在经历一些奇怪的关闭行为。服务器会不时关闭,事件日志中只有此消息,跟踪日志中没有消息,“广播服务意外终止。它已执行此操作1次。” 公共类服务器套接字 维索克 公共事件ClientConnected(ByVal sender作为对象,ByVal e作为EventArgs(属于IClientSocket))实现IServerSocket.ClientConnected 专用插座作为插座 作为IBroadcasterServiceSettingsSection的专

我有一个Windows服务中的类,它正在经历一些奇怪的关闭行为。服务器会不时关闭,事件日志中只有此消息,跟踪日志中没有消息,“广播服务意外终止。它已执行此操作1次。”

公共类服务器套接字
维索克
公共事件ClientConnected(ByVal sender作为对象,ByVal e作为EventArgs(属于IClientSocket))实现IServerSocket.ClientConnected
专用插座作为插座
作为IBroadcasterServiceSettingsSection的专用只读设置
专用只读跟踪源作为ITraceSource
公共分新()
Me.New(BroadcasterServiceSettingsSection.GetSection,BroadcasterTraceSource.Instance)
端接头
Public Sub New(ByVal设置为IBroadCaster服务设置部分,ByVal traceSource为ITraceSource)
_设置=设置
_traceSource=traceSource
端接头
Public Sub Listen()实现IServerSocket.Listen
将端点设置为新的IPEndPoint(System.Net.IPAddress.Parse(\u settings.BroadcasterIPAddress),\u settings.BroadcasterPortNumber)
尝试
_套接字=新套接字(AddressFamily.InterNetwork、SocketType.Stream、ProtocolType.Tcp)
_socket.SetSocketOption(SocketOptionLevel.socket,SocketOptionName.ReuseAddress,1)
_socket.Bind(端点)
_socket.Listen(SocketOptionName.MaxConnections)
_BeginAccept(新的AsyncCallback(AcceptCallback的地址),Nothing)
_traceSource.TraceInformation(“正在侦听新客户端的服务器套接字”)
特例
_traceSource.TraceCritical(“ServerSocket caughtException正在尝试等待新客户端。”)
投手
结束尝试
端接头
''' 
''第一次尝试关闭套接字以清除剩余的发送或接收数据。然后关闭
''套接字以释放所有连接并清理非托管资源。另请参见插座。关机
''和插座,关上
''' 
Public Sub Close()实现IServerSocket.Close
尝试
_socket.Shutdown(SocketShutdown.Both)
特例
_traceSource.TraceEvent(TraceEventType.Error,“关闭服务器套接字导致异常”,例如Message,例如StackTrace)
结束尝试
尝试
_socket.Close()
特例
_traceSource.TraceEvent(TraceEventType.Error,“关闭服务器套接字导致异常”,例如Message,例如StackTrace)
结束尝试
_traceSource.TraceEvent(TraceEventType.Information,“服务器套接字已关闭”)
端接头
专用子AcceptCallback(ByVal ar作为IAsyncResult)
将s调暗为插座=无
尝试
s=_socket.EndAccept(ar)
特例
_traceSource.TraceInformation(“ServerSocket在尝试为客户端获取新套接字时捕获异常。”,例如Message,例如StackTrace)
结束尝试
尝试
'尽快调用begin accept,以便获得下一个传入的客户端
_BeginAccept(新的AsyncCallback(AcceptCallback的地址),Nothing)
特例
_traceSource.TraceeEvent(TraceEventType.Critical,“ServerSocket caughtException正在尝试等待新客户端”,例如Message,例如StackTrace)
结束尝试
尝试
如果s不是什么,那么
将clientSocket设置为IClientSocket=新的clientSocket
OnClient已连接(新事件参数(IClientSocket)(clientSocket))
如果结束
特例
_traceSource.TraceEvent(TraceEventType.Critical,“9/23审查:”+ex.ToString())
结束尝试
端接头
已连接的专用子OnClient(通过Val e作为(IClientSocket的)事件参数)
RaiseEvent客户端已连接(Me、e)
端接头
末级

关于这个类,我最感兴趣的一点是,在调用_socket.EndAccept之后,立即调用_socket.beginacept,然后完成“客户机套接字”的工作。我摸不着,但这闻起来不对劲。用于侦听新连接的套接字是否应保留为字段?如果没有,您以后会如何调用关机?这是一个运行时间很长(数周/数月)的过程。

发布的代码不会导致任何未经处理的异常,从而导致服务器关闭。嗯,是的,但只有在听的时候

另外,不要写入
Throw ex
,它会破坏原始堆栈跟踪<代码>抛出就足够了


至于EndAccept/BeginAccept/HandleEvent,这没有什么错。

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

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

\u socket
是您的代码用来侦听所有传入连接的套接字

这里有一个详细的描述,说明了这一切是如何完成的


我看不出套接字处理逻辑有任何问题。我建议您在服务中附加一个调试器,并尝试在退出时确定上下文是什么。

附加调试器是一个难题。服务不会很快失败。它可能运行4周或4小时,然后突然消失。错误日志中没有错误,跟踪日志中也没有错误
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