Files
Modules/EDMIAPI/Client/Channel.vb
Developer01 ed655879f9 Verbesserung von Stabilität und Fehlerbehandlung
- 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`.
2026-09-22 16:36:20 +02:00

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