- Implementierung von `IDisposable` in der Klasse `Client`, um Ressourcen wie Timer und Kanäle ordnungsgemäß freizugeben. - Einführung von `ActiveClients` und `ClientStatus` zur Verfolgung und Statusanzeige aktiver Clients. - Verbesserte Fehlerbehandlung mit der Methode `HandleCommunicationError`, die Kommunikationsfehler protokolliert, den Client offline setzt und einen Reconnect auslöst. - Optimierung der Reconnect-Logik mit asynchronen Wiederverbindungsversuchen und gestaffelten Verzögerungen. - Erweiterung der Konstruktoren in `Client` um einen optionalen `pClientName`-Parameter und Generierung eines eindeutigen `ClientId`. - Verbesserte Timer-Logik und asynchrone Verarbeitung in `UpdateTimer_Elapsed`. - Einführung der Methode `EnsureAvailable`, um sicherzustellen, dass der Kanal vor Service-Aufrufen verfügbar ist. - Hinzufügen asynchroner Varianten für Service-Methoden (`GetDatatable`, `GetScalarValue`, `ExecuteNonQuery`) und Fallback auf direkte Datenbankzugriffe. - Verbesserte Timeout-Einstellungen und Verbindungsverwaltung in der Klasse `Channel`. - Erweiterungen in `DatabaseWithFallback` für asynchrone Methoden und detaillierteres Logging. - Aktualisierung der Assembly-Version auf `2.7.0`.
124 lines
5.3 KiB
VB.net
124 lines
5.3 KiB
VB.net
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
|
|
''' <summary>
|
|
''' Closes a channel gracefully, falls back to Abort on error
|
|
''' </summary>
|
|
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
|
|
''' <summary>
|
|
''' Creates a channel and adds a Faulted-Handler
|
|
''' </summary>
|
|
''' <returns>A channel object</returns>
|
|
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
|
|
|
|
|