Imports System.ServiceModel
Imports System.Threading
Imports System.Xml
Imports DigitalData.Modules.Base
Imports DigitalData.Modules.EDMI.API.EDMIServiceReference
Imports DigitalData.Modules.Logging
Public Class Channel
Inherits BaseClass
Private ReadOnly ChannelFactory As ChannelFactory(Of IEDMIServiceChannel)
Public Event Reconnect As EventHandler
' Verbindungszähler
Private Shared _ActiveConnections As Integer = 0
Public Shared ReadOnly Property ActiveConnections As Integer
Get
Return _ActiveConnections
End Get
End Property
Public Shared Function GetBinding(Optional AuthenticationMode As TcpClientCredentialType = TcpClientCredentialType.Windows) As NetTcpBinding
Return New NetTcpBinding() With {
.MaxReceivedMessageSize = Constants.ChannelSettings.MAX_RECEIVED_MESSAGE_SIZE,
.MaxBufferSize = Constants.ChannelSettings.MAX_BUFFER_SIZE,
.MaxBufferPoolSize = Constants.ChannelSettings.MAX_BUFFER_POOL_SIZE,
.ListenBacklog = Constants.ChannelSettings.MAX_CONNECTIONS,
.MaxConnections = Constants.ChannelSettings.MAX_CONNECTIONS,
.TransferMode = TransferMode.Streamed,
.SendTimeout = TimeSpan.FromSeconds(30), ' ← War: 5min → Schnellere Fehlererkennung
.ReceiveTimeout = TimeSpan.FromMinutes(10), ' Keepalive < Firewall-Timeout (3600s) ✓
.OpenTimeout = TimeSpan.FromSeconds(30), ' ← Neu: Verbindungsaufbau begrenzen
.CloseTimeout = TimeSpan.FromSeconds(30),
.Security = New NetTcpSecurity() With {
.Mode = SecurityMode.Transport,
.Transport = New TcpTransportSecurity() With {
.ClientCredentialType = AuthenticationMode
}
},
.ReaderQuotas = New XmlDictionaryReaderQuotas() With {
.MaxArrayLength = Constants.ChannelSettings.MAX_ARRAY_LENGTH,
.MaxStringContentLength = Constants.ChannelSettings.MAX_STRING_CONTENT_LENGTH
}
}
End Function
Public Sub New(pLogConfig As LogConfig, pServerAddress As ServerAddressStruct)
MyBase.New(pLogConfig)
ChannelFactory = GetChannelFactory(pServerAddress)
End Sub
'''
''' Closes a channel gracefully, falls back to Abort on error
'''
Public Shared Sub CloseChannel(pChannel As IEDMIServiceChannel)
If pChannel Is Nothing Then Return
Try
If pChannel.State = CommunicationState.Opened Then
pChannel.Close()
ElseIf pChannel.State = CommunicationState.Faulted OrElse
pChannel.State = CommunicationState.Closing Then
pChannel.Abort()
End If
' CommunicationState.Created / Closed → nichts tun, Zähler nicht betroffen
Catch ex As Exception
Try
pChannel.Abort()
Catch
' Ignore
End Try
End Try
End Sub
'''
''' Creates a channel and adds a Faulted-Handler
'''
''' A channel object
Public Function GetChannel() As IEDMIServiceChannel
Try
Logger.Debug("Creating channel.")
Dim oChannel = ChannelFactory.CreateChannel()
' Verbindung zählen beim Öffnen
AddHandler oChannel.Opened, Sub()
Dim oCount = Interlocked.Increment(_ActiveConnections)
Logger.Debug("Channel opened. Active connections: [{0}]", oCount)
End Sub
' Verbindung abziehen beim Schließen oder Fehler
AddHandler oChannel.Closed, Sub()
Dim oCount = Interlocked.Decrement(_ActiveConnections)
Logger.Debug("Channel closed. Active connections: [{0}]", oCount)
End Sub
AddHandler oChannel.Faulted, Sub()
Dim oCount = Interlocked.Decrement(_ActiveConnections)
Logger.Warn("Channel faulted. Active connections: [{0}]", oCount)
RaiseEvent Reconnect(Me, Nothing)
End Sub
Logger.Debug("Channel created. State=[{0}]", oChannel.State)
Return oChannel
Catch ex As Exception
Logger.Error(ex)
Throw
End Try
End Function
Private Function GetChannelFactory(pServerAddress As ServerAddressStruct) As ChannelFactory(Of IEDMIServiceChannel)
Dim oBinding = GetBinding()
Dim oAddress = New EndpointAddress($"net.tcp://{pServerAddress.Host}:{pServerAddress.Port}/DigitalData/Services/Main")
Logger.Debug("Creating ChannelFactory with Binding: SendTimeout=[{0}], ReceiveTimeout=[{1}], MaxConnections=[{2}], MaxBufferSize=[{3}], TransferMode=[{4}]",
oBinding.SendTimeout,
oBinding.ReceiveTimeout,
oBinding.MaxConnections,
oBinding.MaxBufferSize,
oBinding.TransferMode)
Dim oFactory = New ChannelFactory(Of IEDMIServiceChannel)(oBinding, oAddress)
Return oFactory
End Function
End Class