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