Updated README.md
This commit is contained in:
@@ -1,20 +1,385 @@
|
|||||||
|
|
||||||
# EonaCat.Connections
|
# EonaCat.Connections
|
||||||
.NET Framework 4.8+ / .NET (Core) compatible library providing high-throughput TCP/UDP
|
|
||||||
servers and clients with optional TLS (for TCP) and optional application-layer encryption (TCP/UDP).
|
|
||||||
|
|
||||||
## Design goals:
|
A high-performance, production-ready .NET Framework 4.8+ / .NET 8 compatible library for building scalable TCP/UDP servers and clients with optional TLS encryption and application-layer AES encryption.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
### Core Capabilities
|
||||||
|
- **High Performance**: Event-driven, asynchronous architecture with minimal allocations
|
||||||
|
- **Massive Scalability**: Handles tens of thousands of concurrent connections using SocketAsyncEventArgs (SAEA)
|
||||||
|
- **Dual Protocol Support**: TCP and UDP with independent or mixed configurations
|
||||||
|
- **TLS/SSL Encryption**: Secure TCP connections using SslStream with certificate support
|
||||||
|
- **Application-Layer Encryption**: AES-CBC + PBKDF2_SHA256 encryption for both TCP and UDP payloads
|
||||||
|
- **Low Latency**: Optimized for millisecond-level response times
|
||||||
|
|
||||||
|
### Advanced Features
|
||||||
|
- **Auto-Reconnection**: Automatic client reconnection with configurable retry logic and exponential backoff
|
||||||
|
- **Heartbeat/Ping-Pong**: Keep-alive mechanism to detect dead connections
|
||||||
|
- **Idle Detection**: Automatic detection and handling of idle connections with configurable timeout
|
||||||
|
- **Message Framing**: Support for delimiter-based, length-prefixed, or raw message modes
|
||||||
|
- **Request-Response Patterns**: Send message and wait for response with timeout support
|
||||||
|
- **Broadcast Messaging**: Send to all connected clients simultaneously
|
||||||
|
- **Client-to-Client Messaging**: Direct peer communication through the server
|
||||||
|
- **Nicknames**: Optional client identification system
|
||||||
|
- **Health API**: Lightweight REST API for monitoring server health and statistics
|
||||||
|
- **Status Reporting**: Auto-generated HTML status pages showing connected clients and throughput
|
||||||
|
- **Network Monitoring**: Built-in network health monitoring and outage detection
|
||||||
|
- **SSL Diagnostics**: Detailed SSL handshake diagnostics and performance metrics
|
||||||
|
- **Connection Management**: Full lifecycle management with detailed disconnect reasons
|
||||||
|
|
||||||
|
## Design Goals
|
||||||
|
|
||||||
- High performance and low latency
|
- High performance and low latency
|
||||||
- Scalable to tens of thousands of concurrent connections
|
- Scalable to tens of thousands of concurrent connections
|
||||||
- Scalable socket I/O via SocketAsyncEventArgs (SAEA) for raw TCP and UDP
|
- Scalable socket I/O via SocketAsyncEventArgs (SAEA) for raw TCP and UDP
|
||||||
- TLS (SSL) over TCP using SslStream (built-in)
|
- TLS (SSL) over TCP using SslStream (built-in)
|
||||||
- Optional encryption (AES-CBC + PBKDF2_SHA256) for TCP/UDP payloads
|
- Optional encryption (AES-CBC + PBKDF2_SHA256) for TCP/UDP payloads
|
||||||
- Minimal allocations, event-driven callbacks
|
- Minimal allocations, event-driven callbacks
|
||||||
|
- For highest throughput, run x64, enable LargePage, set appropriate Socket options and OS registry tuning
|
||||||
|
|
||||||
#### - For highest throughput, run x64, enable LargePage, set appropriate Socket options and OS registry tuning.
|
---
|
||||||
|
|
||||||
## Generate self-signed certificate for TLS (TCP):
|
## Configuration
|
||||||
### Run as Administrator
|
|
||||||
|
The `Configuration` class provides extensive customization options:
|
||||||
|
|
||||||
|
### Connection Settings
|
||||||
|
- **Protocol**: `TCP` or `UDP` (default: TCP)
|
||||||
|
- **Host**: Server address for clients (default: 127.0.0.1)
|
||||||
|
- **Port**: Network port (default: 8080)
|
||||||
|
- **MaxConnections**: Maximum concurrent connections (default: 100000)
|
||||||
|
- **ConnectionTimeout**: Timeout for connection attempts (default: 30s)
|
||||||
|
|
||||||
|
### TLS/SSL Settings
|
||||||
|
- **Certificate**: X509Certificate2 for server (enables TLS when set)
|
||||||
|
- **AdditionalCertificates**: Additional certificates for chain validation
|
||||||
|
- **IsSelfSignedEnabled**: Allow self-signed certificates (testing only)
|
||||||
|
- **CheckCertificateRevocation**: Validate certificate revocation status
|
||||||
|
- **MutuallyAuthenticate**: Require mutual TLS authentication
|
||||||
|
- **UseSsl**: Read-only property (true if Certificate is set)
|
||||||
|
- **EnableSslDiagnostics**: Enable detailed SSL handshake diagnostics
|
||||||
|
- **AllowTlsRenegotiation**: Allow TLS renegotiation
|
||||||
|
|
||||||
|
### SSL Retry & Handshake Configuration
|
||||||
|
- **SSLMaxRetries**: Maximum SSL handshake retry attempts (0 = unlimited, default: 0)
|
||||||
|
- **SSLTimeoutInSeconds**: SSL handshake timeout (default: 35)
|
||||||
|
- **SSLRetryDelayInSeconds**: Delay between SSL retries (default: 5)
|
||||||
|
- **UseExponentialBackoffForSslRetries**: Enable exponential backoff (default: false)
|
||||||
|
- **SSLRetryDelayMaxSeconds**: Maximum retry delay when exponential backoff enabled (default: 60)
|
||||||
|
|
||||||
|
### Encryption Settings
|
||||||
|
- **UseAesEncryption**: Enable AES-CBC encryption (default: false)
|
||||||
|
- **AesPassword**: Password for AES key derivation
|
||||||
|
- **UseBigEndian**: Use big-endian byte order (default: false)
|
||||||
|
|
||||||
|
### Keep-Alive & Heartbeat
|
||||||
|
- **EnableKeepAlive**: Enable TCP keep-alive probes (default: true)
|
||||||
|
- **KeepAliveTimeSeconds**: Time before first keep-alive probe (default: 60)
|
||||||
|
- **KeepAliveIntervalSeconds**: Interval between probes (default: 10)
|
||||||
|
- **KeepAliveRetryCount**: Unacked probes before disconnect (default: 10)
|
||||||
|
- **EnableHeartbeat**: Enable ping-pong heartbeat (default: false)
|
||||||
|
- **HeartbeatIntervalSeconds**: Heartbeat interval (default: 5)
|
||||||
|
- **EnablePingPongLogs**: Log ping-pong messages (default: false)
|
||||||
|
- **DisconectOnMissedPong**: Disconnect on missed pong response (default: false)
|
||||||
|
|
||||||
|
### Message Framing
|
||||||
|
- **MessageFraming**: `None`, `Delimiter`, or `LengthPrefixed` (default: None)
|
||||||
|
- **Delimiter**: Message separator bytes (default: %)
|
||||||
|
- **LengthPrefixedLength**: Prefix byte count for length-prefixed mode (default: 4)
|
||||||
|
- **MAX_MESSAGE_SIZE**: Maximum message size (default: 100 MB)
|
||||||
|
|
||||||
|
### Idle Detection & Timeout
|
||||||
|
- **IdleTimeoutSeconds**: Idle connection timeout, 0 = disabled (default: 30)
|
||||||
|
|
||||||
|
### Socket Configuration
|
||||||
|
- **BufferSize**: Socket buffer size (default: ExtraLarge ~65K)
|
||||||
|
- **EnableNagle**: Enable Nagle's algorithm (default: false)
|
||||||
|
- **EnableRST**: Send RST flag on close (default: false)
|
||||||
|
|
||||||
|
### Auto-Reconnection (Client)
|
||||||
|
- **EnableAutoReconnect**: Auto-reconnect on disconnect (default: true)
|
||||||
|
- **ReconnectDelayInSeconds**: Delay before reconnection attempt (default: 5)
|
||||||
|
- **MaxReconnectAttempts**: Max reconnect attempts, 0 = unlimited (default: 0)
|
||||||
|
|
||||||
|
### Logging & Diagnostics
|
||||||
|
- **EnableConnectionDebugLogs**: Log connection lifecycle events (default: false)
|
||||||
|
|
||||||
|
### HTML Status & Health API
|
||||||
|
- **EnableAutoHtmlReports**: Generate periodic HTML error reports (default: false)
|
||||||
|
- **HtmlReportOutputDirectory**: Report output directory (default: ./reports)
|
||||||
|
- **HtmlReportIntervalSeconds**: Report generation interval (default: 60)
|
||||||
|
- **EnableServerStatusPage**: Generate HTML status page with client list (default: false)
|
||||||
|
- **ServerStatusPageIntervalSeconds**: Status page update interval (default: 5)
|
||||||
|
- **EnableHealthApi**: Start lightweight REST health API (default: false)
|
||||||
|
- **HealthApiPort**: Health API port, 0 = random (default: 0)
|
||||||
|
- **HealthApiBindAddress**: Health API bind address (default: 127.0.0.1)
|
||||||
|
|
||||||
|
### Certificate Management
|
||||||
|
- **TrustedThumbprints**: List of trusted certificate thumbprints for validation
|
||||||
|
- **CheckAgainstInternalTrustedCertificates**: Validate against Windows certificate store (default: true)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## NetworkServer API
|
||||||
|
|
||||||
|
### Events
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Connection events
|
||||||
|
public event EventHandler<ConnectionEventArgs> OnConnected;
|
||||||
|
public event EventHandler<ConnectionEventArgs> OnConnectedWithNickname;
|
||||||
|
public event EventHandler<ConnectionEventArgs> OnDisconnected;
|
||||||
|
|
||||||
|
// Data events
|
||||||
|
public event EventHandler<DataReceivedEventArgs> OnDataReceived;
|
||||||
|
|
||||||
|
// Error events
|
||||||
|
public event EventHandler<ErrorEventArgs> OnSslError;
|
||||||
|
public event EventHandler<ErrorEventArgs> OnEncryptionError;
|
||||||
|
public event EventHandler<ErrorEventArgs> OnGeneralError;
|
||||||
|
public event EventHandler<ErrorEventArgs> OnSocketError;
|
||||||
|
|
||||||
|
// Heartbeat events
|
||||||
|
public event EventHandler<PingEventArgs> OnClientPingResponse;
|
||||||
|
public event EventHandler<PingEventArgs> OnPongMissed;
|
||||||
|
|
||||||
|
// Idle/Timeout events
|
||||||
|
public event EventHandler<IdleEventArgs> OnIdleTimeout;
|
||||||
|
|
||||||
|
// Logging
|
||||||
|
public event EventHandler<string> OnLog;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Properties
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Server Status
|
||||||
|
public bool IsStarted { get; }
|
||||||
|
public int Port { get; }
|
||||||
|
public int ActiveConnections { get; }
|
||||||
|
public int MaxConnections { get; }
|
||||||
|
public ProtocolType Protocol { get; }
|
||||||
|
|
||||||
|
// Security Status
|
||||||
|
public bool IsSecure { get; } // TLS or AES enabled
|
||||||
|
public bool IsEncrypted { get; } // AES enabled
|
||||||
|
|
||||||
|
// Statistics
|
||||||
|
public long DroppedPackets { get; }
|
||||||
|
public long DroppedConnections { get; }
|
||||||
|
public long TotalConnections { get; }
|
||||||
|
public long BytesSent { get; }
|
||||||
|
public long BytesReceived { get; }
|
||||||
|
public long MessagesSent { get; }
|
||||||
|
public long MessagesReceived { get; }
|
||||||
|
public double MessagesPerSecond { get; }
|
||||||
|
public TimeSpan Uptime { get; }
|
||||||
|
public DateTime StartTime { get; }
|
||||||
|
public DateTime LastDataSent { get; }
|
||||||
|
public DateTime LastDataReceived { get; }
|
||||||
|
|
||||||
|
// Status Pages
|
||||||
|
public ServerStatusPage ServerStatus { get; }
|
||||||
|
public SocketStatusPage StatusPage { get; }
|
||||||
|
public HealthApiServer HealthApi { get; }
|
||||||
|
|
||||||
|
// Debug
|
||||||
|
public bool DEBUG_DATA_SEND { get; set; }
|
||||||
|
public bool DEBUG_DATA_RECEIVED { get; set; }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Core Methods
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Lifecycle
|
||||||
|
public Task StartAsync();
|
||||||
|
public void Stop();
|
||||||
|
public async Task StopAsync();
|
||||||
|
public Task StartTcpServerAsync();
|
||||||
|
public async Task StartUdpServerAsync(CancellationToken token);
|
||||||
|
public async Task StopTcpServerAsync();
|
||||||
|
|
||||||
|
// Client Management
|
||||||
|
public Dictionary<string, Connection> GetClients();
|
||||||
|
public List<Connection> GetClient(string clientId);
|
||||||
|
public async Task DisconnectClientAsync(string clientId, DisconnectReason reason, Exception? exception);
|
||||||
|
|
||||||
|
// Messaging - Send to Specific Client
|
||||||
|
public async Task<bool> SendToClientAsync(string clientId, byte[] data);
|
||||||
|
public async Task<bool> SendToClientAsync(string clientId, string message);
|
||||||
|
|
||||||
|
// Messaging - Client-to-Client
|
||||||
|
public async Task<bool> SendFromClientToClientAsync(string fromClientId, string toClientId, byte[] data);
|
||||||
|
public async Task<bool> SendFromClientToClientAsync(string fromClientId, string toClientId, string message);
|
||||||
|
|
||||||
|
// Broadcast Messaging
|
||||||
|
public async Task<bool> BroadcastAsync(byte[] data);
|
||||||
|
public async Task<bool> BroadcastAsync(string message);
|
||||||
|
|
||||||
|
// Request-Response Patterns
|
||||||
|
public async Task<DataReceivedEventArgs> SendToClientAndWaitForResponseAsync(
|
||||||
|
string clientId, byte[] data, TimeSpan? timeout = null);
|
||||||
|
public async Task<DataReceivedEventArgs> SendToClientAndWaitForResponseAsync(
|
||||||
|
string clientId, string message, TimeSpan? timeout = null);
|
||||||
|
|
||||||
|
public async Task<DataReceivedEventArgs> SendFromClientToClientAndWaitForResponseAsync(
|
||||||
|
string fromClientId, string toClientId, byte[] data, TimeSpan? timeout = null);
|
||||||
|
public async Task<DataReceivedEventArgs> SendFromClientToClientAndWaitForResponseAsync(
|
||||||
|
string fromClientId, string toClientId, string message, TimeSpan? timeout = null);
|
||||||
|
|
||||||
|
public async Task<List<DataReceivedEventArgs>> BroadcastAndWaitForResponsesAsync(
|
||||||
|
byte[] data, TimeSpan? timeout = null);
|
||||||
|
public async Task<List<DataReceivedEventArgs>> BroadcastAndWaitForResponsesAsync(
|
||||||
|
string message, TimeSpan? timeout = null);
|
||||||
|
|
||||||
|
// Configuration
|
||||||
|
public void SetSeparator(string separator);
|
||||||
|
public void SetSeparator(byte[] separator);
|
||||||
|
|
||||||
|
// Statistics
|
||||||
|
public Stats GetStats();
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
public void Dispose();
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## NetworkClient API
|
||||||
|
|
||||||
|
### Events
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Connection events
|
||||||
|
public event EventHandler<ConnectionEventArgs> OnConnected;
|
||||||
|
public event EventHandler<ConnectionEventArgs> OnNicknameSend;
|
||||||
|
public event EventHandler<ConnectionEventArgs> OnDisconnected;
|
||||||
|
|
||||||
|
// Data events
|
||||||
|
public event EventHandler<DataReceivedEventArgs> OnDataReceived;
|
||||||
|
|
||||||
|
// Error events
|
||||||
|
public event EventHandler<ErrorEventArgs> OnSslError;
|
||||||
|
public event EventHandler<ErrorEventArgs> OnEncryptionError;
|
||||||
|
public event EventHandler<ErrorEventArgs> OnGeneralError;
|
||||||
|
public event EventHandler<ErrorEventArgs> OnSocketError;
|
||||||
|
|
||||||
|
// Heartbeat events
|
||||||
|
public event EventHandler<PingEventArgs> OnPingResponse;
|
||||||
|
public event EventHandler<PingEventArgs> OnPongResponse;
|
||||||
|
|
||||||
|
// Idle/Timeout events
|
||||||
|
public event EventHandler<IdleClientEventArgs> OnIdleTimeout;
|
||||||
|
|
||||||
|
// Logging
|
||||||
|
public event EventHandler<string> OnLog;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Properties
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Connection Status
|
||||||
|
public bool IsConnected { get; set; }
|
||||||
|
public int Port { get; }
|
||||||
|
|
||||||
|
// Connection Time Metrics
|
||||||
|
public DateTime ConnectionTime { get; }
|
||||||
|
public TimeSpan Uptime { get; }
|
||||||
|
public DateTime LastActive { get; }
|
||||||
|
public int IdleTimeInSeconds();
|
||||||
|
public int IdleTimeInMinutes();
|
||||||
|
public int IdleTimeInHours();
|
||||||
|
public int IdleTimeInDays();
|
||||||
|
public int ConnectedTimeInSeconds();
|
||||||
|
public int ConnectedTimeInMinutes();
|
||||||
|
public int ConnectedTimeInHours();
|
||||||
|
public int ConnectedTimeInDays();
|
||||||
|
|
||||||
|
// Security Status
|
||||||
|
public bool IsSecure { get; } // TLS or AES enabled
|
||||||
|
public bool IsEncrypted { get; } // AES enabled
|
||||||
|
public bool IsTcp { get; }
|
||||||
|
|
||||||
|
// Statistics
|
||||||
|
public long BytesSent { get; }
|
||||||
|
public long BytesReceived { get; }
|
||||||
|
public long MessagesSent { get; }
|
||||||
|
public long MessagesReceived { get; }
|
||||||
|
|
||||||
|
// Heartbeat
|
||||||
|
public bool DisconnectOnMissedPong { get; set; }
|
||||||
|
public bool EnableKeepAlive { get; set; }
|
||||||
|
|
||||||
|
// Auto-Reconnect
|
||||||
|
public bool IsAutoConnectStarted { get; }
|
||||||
|
public bool IsIdleTimeoutTriggered { get; }
|
||||||
|
|
||||||
|
// Debug
|
||||||
|
public bool DEBUG_DATA_SEND { get; set; }
|
||||||
|
public bool DEBUG_DATA_RECEIVED { get; set; }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Core Methods
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Connection Lifecycle
|
||||||
|
public async Task<bool> ConnectAsync();
|
||||||
|
public async Task DisconnectAsync();
|
||||||
|
public async Task DisconnectDueTimeoutAsync();
|
||||||
|
|
||||||
|
// Messaging
|
||||||
|
public async Task<bool> SendAsync(byte[] data);
|
||||||
|
public async Task<bool> SendAsync(string message);
|
||||||
|
|
||||||
|
// Nickname
|
||||||
|
public async Task<bool> SendNicknameAsync(string nickname);
|
||||||
|
|
||||||
|
// Request-Response
|
||||||
|
public async Task<DataReceivedEventArgs> SendAndWaitForResponseAsync(
|
||||||
|
byte[] data, TimeSpan? timeout = null);
|
||||||
|
public async Task<DataReceivedEventArgs> SendAndWaitForResponseAsync(
|
||||||
|
string message, TimeSpan? timeout = null);
|
||||||
|
public async Task<DataReceivedEventArgs> SendNicknameAndWaitForResponseAsync(
|
||||||
|
string nickname, TimeSpan? timeout = null);
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
public void Dispose();
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Message Framing Modes
|
||||||
|
|
||||||
|
Three message framing modes are supported:
|
||||||
|
|
||||||
|
### 1. None (Default)
|
||||||
|
Raw bytes sent as-is, no frame boundaries marked:
|
||||||
|
```csharp
|
||||||
|
config.MessageFraming = FramingMode.None;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Delimiter
|
||||||
|
Messages separated by a delimiter byte sequence (default: %):
|
||||||
|
```csharp
|
||||||
|
config.MessageFraming = FramingMode.Delimiter;
|
||||||
|
config.Delimiter = Encoding.UTF8.GetBytes("%");
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Length-Prefixed
|
||||||
|
Messages prefixed with their length as bytes:
|
||||||
|
```csharp
|
||||||
|
config.MessageFraming = FramingMode.LengthPrefixed;
|
||||||
|
config.LengthPrefixedLength = 4; // 4-byte prefix
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SSL/TLS Certificate Setup
|
||||||
|
|
||||||
|
### Generate self-signed certificate for TLS (TCP):
|
||||||
|
Run the following in PowerShell as Administrator:
|
||||||
|
|
||||||
|
```powershell
|
||||||
$cert = New-SelfSignedCertificate `
|
$cert = New-SelfSignedCertificate `
|
||||||
-DnsName "localhost" `
|
-DnsName "localhost" `
|
||||||
-CertStoreLocation "Cert:\LocalMachine\My" `
|
-CertStoreLocation "Cert:\LocalMachine\My" `
|
||||||
@@ -28,34 +393,120 @@ servers and clients with optional TLS (for TCP) and optional application-layer e
|
|||||||
-Cert "Cert:\LocalMachine\My\$($cert.Thumbprint)" `
|
-Cert "Cert:\LocalMachine\My\$($cert.Thumbprint)" `
|
||||||
-FilePath "C:\temp\server.pfx" `
|
-FilePath "C:\temp\server.pfx" `
|
||||||
-Password $password
|
-Password $password
|
||||||
|
```
|
||||||
|
|
||||||
#### This will create a self-signed certificate with the password 'p@ss' in the folder C:\temp\server.pfx.
|
This creates a self-signed certificate with password `p@ss` in `C:\temp\server.pfx`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Server example:
|
## Advanced Features
|
||||||
|
|
||||||
|
### Auto-Reconnection
|
||||||
|
The client automatically reconnects on disconnect:
|
||||||
|
```csharp
|
||||||
|
config.EnableAutoReconnect = true; // Enable auto-reconnect
|
||||||
|
config.ReconnectDelayInSeconds = 5; // Delay before retry
|
||||||
|
config.MaxReconnectAttempts = 0; // 0 = unlimited attempts
|
||||||
|
```
|
||||||
|
|
||||||
|
### Heartbeat (Ping-Pong)
|
||||||
|
Keep connections alive and detect dead connections:
|
||||||
|
```csharp
|
||||||
|
config.EnableHeartbeat = true;
|
||||||
|
config.HeartbeatIntervalSeconds = 5;
|
||||||
|
config.DisconectOnMissedPong = true; // Disconnect if pong not received
|
||||||
|
```
|
||||||
|
|
||||||
|
### Idle Detection
|
||||||
|
Automatically disconnect idle connections:
|
||||||
|
```csharp
|
||||||
|
config.IdleTimeoutSeconds = 30; // 0 = disabled
|
||||||
|
```
|
||||||
|
|
||||||
|
### Request-Response Patterns
|
||||||
|
Send a message and wait for a response with timeout:
|
||||||
|
```csharp
|
||||||
|
// Server sends message and waits for client response
|
||||||
|
var response = await server.SendToClientAndWaitForResponseAsync(
|
||||||
|
clientId, "question", TimeSpan.FromSeconds(5));
|
||||||
|
|
||||||
|
if (response != null)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Got response: {response.StringData}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client sends message and waits for server response
|
||||||
|
var response = await client.SendAndWaitForResponseAsync(
|
||||||
|
"answer", TimeSpan.FromSeconds(5));
|
||||||
|
```
|
||||||
|
|
||||||
|
### Health API
|
||||||
|
Enable a lightweight REST API for server monitoring:
|
||||||
|
```csharp
|
||||||
|
config.EnableHealthApi = true;
|
||||||
|
config.HealthApiPort = 0; // Random port
|
||||||
|
config.HealthApiBindAddress = IPAddress.Any; // For Docker/containers
|
||||||
|
|
||||||
|
// After server starts, access the API:
|
||||||
|
// http://localhost:{HealthApi.Port}/health
|
||||||
|
// http://localhost:{HealthApi.Port}/stats
|
||||||
|
```
|
||||||
|
|
||||||
|
### Status Pages
|
||||||
|
Auto-generate HTML status reports:
|
||||||
|
```csharp
|
||||||
|
config.EnableServerStatusPage = true;
|
||||||
|
config.ServerStatusPageIntervalSeconds = 5;
|
||||||
|
|
||||||
|
config.EnableAutoHtmlReports = true;
|
||||||
|
config.HtmlReportOutputDirectory = "./reports";
|
||||||
|
config.HtmlReportIntervalSeconds = 60;
|
||||||
|
```
|
||||||
|
|
||||||
|
### SSL Diagnostics
|
||||||
|
Enable detailed SSL handshake diagnostics:
|
||||||
|
```csharp
|
||||||
|
config.EnableSslDiagnostics = true;
|
||||||
|
// Access via: server.StatusPage.SslMetrics
|
||||||
|
```
|
||||||
|
|
||||||
|
### Network Monitoring
|
||||||
|
Monitor network health and detect outages:
|
||||||
|
```csharp
|
||||||
|
var monitor = new NetworkMonitor();
|
||||||
|
monitor.OnNetworkOutageDetected += (s, e) =>
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Network outage detected: {e.Duration}");
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Server Example
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using EonaCat.Connections;
|
||||||
using EonaCat.Connections.Models;
|
using EonaCat.Connections.Models;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
|
||||||
namespace EonaCat.Connections.Server.Example
|
namespace EonaCat.Connections.Server.Example
|
||||||
{
|
{
|
||||||
// This file is part of the EonaCat project(s) which is released under the Apache License.
|
|
||||||
// See the LICENSE file or go to https://EonaCat.com/license for full license details.
|
|
||||||
|
|
||||||
public class Program
|
public class Program
|
||||||
{
|
{
|
||||||
private static NetworkServer _server;
|
private static NetworkServer _server;
|
||||||
|
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
CreateServerAsync().ConfigureAwait(false);
|
CreateServerAsync().GetAwaiter().GetResult();
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
Console.Write("Enter message to send (or 'exit' to quit): ");
|
Console.Write("Enter message to broadcast (or 'exit' to quit): ");
|
||||||
var message = Console.ReadLine();
|
var message = Console.ReadLine();
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(message) && message.Equals("exit", StringComparison.OrdinalIgnoreCase))
|
if (!string.IsNullOrEmpty(message) && message.Equals("exit", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
_server.Stop();
|
_server.StopAsync().GetAwaiter().GetResult();
|
||||||
_server.Dispose();
|
_server.Dispose();
|
||||||
Console.WriteLine("Server stopped.");
|
Console.WriteLine("Server stopped.");
|
||||||
break;
|
break;
|
||||||
@@ -63,7 +514,7 @@ servers and clients with optional TLS (for TCP) and optional application-layer e
|
|||||||
|
|
||||||
if (!string.IsNullOrEmpty(message))
|
if (!string.IsNullOrEmpty(message))
|
||||||
{
|
{
|
||||||
_server.BroadcastAsync(message).ConfigureAwait(false);
|
_server.BroadcastAsync(message).GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,34 +525,33 @@ servers and clients with optional TLS (for TCP) and optional application-layer e
|
|||||||
{
|
{
|
||||||
Protocol = ProtocolType.TCP,
|
Protocol = ProtocolType.TCP,
|
||||||
Port = 1111,
|
Port = 1111,
|
||||||
UseSsl = true,
|
Certificate = new X509Certificate2("server.pfx", "p@ss"), // Enables TLS
|
||||||
UseAesEncryption = true,
|
UseAesEncryption = true,
|
||||||
MaxConnections = 100000,
|
|
||||||
AesPassword = "EonaCat.Connections.Password",
|
AesPassword = "EonaCat.Connections.Password",
|
||||||
Certificate = new System.Security.Cryptography.X509Certificates.X509Certificate2("server.pfx", "p@ss")
|
MaxConnections = 100000,
|
||||||
|
EnableHeartbeat = true,
|
||||||
|
HeartbeatIntervalSeconds = 5,
|
||||||
|
IdleTimeoutSeconds = 30,
|
||||||
|
EnableHealthApi = true,
|
||||||
|
HealthApiPort = 8888,
|
||||||
};
|
};
|
||||||
|
|
||||||
_server = new NetworkServer(config);
|
_server = new NetworkServer(config);
|
||||||
|
|
||||||
// Subscribe to events
|
// Connection events
|
||||||
_server.OnConnected += (sender, e) =>
|
_server.OnConnected += (sender, e) =>
|
||||||
Console.WriteLine($"Client {e.ClientId} connected from {e.RemoteEndPoint}");
|
Console.WriteLine($"[CONNECTED] Client {e.ClientId} from {e.RemoteEndPoint}");
|
||||||
|
|
||||||
_server.OnConnectedWithNickname += (sender, e) =>
|
_server.OnConnectedWithNickname += (sender, e) =>
|
||||||
Console.WriteLine($"Client {e.ClientId} connected with nickname: {e.Nickname}");
|
Console.WriteLine($"[NICKNAME] Client {e.ClientId} identified as: {e.Nickname}");
|
||||||
|
|
||||||
|
// Data reception
|
||||||
_server.OnDataReceived += async (sender, e) =>
|
_server.OnDataReceived += async (sender, e) =>
|
||||||
{
|
{
|
||||||
if (e.HasNickname)
|
var clientName = e.HasNickname ? e.Nickname : e.ClientId;
|
||||||
{
|
Console.WriteLine($"[DATA] From {clientName}: {(e.IsBinary ? $"{e.Data.Length} bytes" : e.StringData)}");
|
||||||
Console.WriteLine($"Received from {e.Nickname}: {(e.IsBinary ? $"{e.Data.Length} bytes" : e.StringData)}");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Received from {e.ClientId}: {(e.IsBinary ? $"{e.Data.Length} bytes" : e.StringData)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Echo back the message
|
// Echo response
|
||||||
if (e.IsBinary)
|
if (e.IsBinary)
|
||||||
{
|
{
|
||||||
await _server.SendToClientAsync(e.ClientId, e.Data);
|
await _server.SendToClientAsync(e.ClientId, e.Data);
|
||||||
@@ -112,45 +562,62 @@ servers and clients with optional TLS (for TCP) and optional application-layer e
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Disconnection
|
||||||
_server.OnDisconnected += (sender, e) =>
|
_server.OnDisconnected += (sender, e) =>
|
||||||
{
|
{
|
||||||
if (e.HasNickname)
|
var clientName = e.HasNickname ? e.Nickname : e.ClientId;
|
||||||
{
|
Console.WriteLine($"[DISCONNECTED] {clientName} ({e.DisconnectReason})");
|
||||||
Console.WriteLine($"Client {e.Nickname} disconnected");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Client {e.ClientId} disconnected");
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Error handling
|
||||||
|
_server.OnGeneralError += (sender, e) =>
|
||||||
|
Console.WriteLine($"[ERROR] {e.Message}");
|
||||||
|
|
||||||
|
_server.OnSslError += (sender, e) =>
|
||||||
|
Console.WriteLine($"[SSL ERROR] {e.Message}");
|
||||||
|
|
||||||
|
// Idle connections
|
||||||
|
_server.OnIdleTimeout += (sender, e) =>
|
||||||
|
Console.WriteLine($"[IDLE] Client disconnected due to timeout: {e.ClientId}");
|
||||||
|
|
||||||
|
// Heartbeat
|
||||||
|
_server.OnPongMissed += (sender, e) =>
|
||||||
|
Console.WriteLine($"[HEARTBEAT] Pong missed from {e.ClientId}");
|
||||||
|
|
||||||
|
Console.WriteLine("Server starting...");
|
||||||
await _server.StartAsync();
|
await _server.StartAsync();
|
||||||
|
Console.WriteLine($"Server listening on port {_server.Port}");
|
||||||
|
Console.WriteLine($"Health API available on: http://localhost:8888/health");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Client example:
|
---
|
||||||
|
|
||||||
|
## Client Example
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using EonaCat.Connections;
|
||||||
using EonaCat.Connections.Models;
|
using EonaCat.Connections.Models;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
|
||||||
namespace EonaCat.Connections.Client.Example
|
namespace EonaCat.Connections.Client.Example
|
||||||
{
|
{
|
||||||
// This file is part of the EonaCat project(s) which is released under the Apache License.
|
|
||||||
// See the LICENSE file or go to https://EonaCat.com/license for full license details.
|
|
||||||
|
|
||||||
public class Program
|
public class Program
|
||||||
{
|
{
|
||||||
private static NetworkClient _client;
|
private static NetworkClient _client;
|
||||||
|
|
||||||
public static async Task Main(string[] args)
|
public static async Task Main(string[] args)
|
||||||
{
|
{
|
||||||
await CreateClientAsync().ConfigureAwait(false);
|
await CreateClientAsync();
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (!_client.IsConnected)
|
if (!_client.IsConnected)
|
||||||
{
|
{
|
||||||
await Task.Delay(1000).ConfigureAwait(false);
|
Console.WriteLine("Waiting for connection...");
|
||||||
|
await Task.Delay(1000);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,13 +626,23 @@ servers and clients with optional TLS (for TCP) and optional application-layer e
|
|||||||
|
|
||||||
if (!string.IsNullOrEmpty(message) && message.Equals("exit", StringComparison.OrdinalIgnoreCase))
|
if (!string.IsNullOrEmpty(message) && message.Equals("exit", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
await _client.DisconnectAsync().ConfigureAwait(false);
|
await _client.DisconnectAsync();
|
||||||
|
_client.Dispose();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(message))
|
if (!string.IsNullOrEmpty(message))
|
||||||
{
|
{
|
||||||
await _client.SendAsync(message).ConfigureAwait(false);
|
await _client.SendAsync(message);
|
||||||
|
|
||||||
|
// Optional: wait for response
|
||||||
|
var response = await _client.SendAndWaitForResponseAsync(
|
||||||
|
message, TimeSpan.FromSeconds(5));
|
||||||
|
|
||||||
|
if (response != null)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Server response: {response.StringData}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -177,39 +654,279 @@ servers and clients with optional TLS (for TCP) and optional application-layer e
|
|||||||
Protocol = ProtocolType.TCP,
|
Protocol = ProtocolType.TCP,
|
||||||
Host = "127.0.0.1",
|
Host = "127.0.0.1",
|
||||||
Port = 1111,
|
Port = 1111,
|
||||||
UseSsl = true,
|
Certificate = new X509Certificate2("client.pfx", "p@ss"), // For TLS (optional)
|
||||||
UseAesEncryption = true,
|
UseAesEncryption = true,
|
||||||
AesPassword = "EonaCat.Connections.Password",
|
AesPassword = "EonaCat.Connections.Password",
|
||||||
Certificate = new System.Security.Cryptography.X509Certificates.X509Certificate2("client.pfx", "p@ss"),
|
EnableAutoReconnect = true,
|
||||||
|
ReconnectDelayInSeconds = 5,
|
||||||
|
MaxReconnectAttempts = 0, // Unlimited retries
|
||||||
|
EnableHeartbeat = true,
|
||||||
|
HeartbeatIntervalSeconds = 5,
|
||||||
|
IdleTimeoutSeconds = 30,
|
||||||
};
|
};
|
||||||
|
|
||||||
_client = new NetworkClient(config);
|
_client = new NetworkClient(config);
|
||||||
|
|
||||||
|
// Error handling
|
||||||
_client.OnGeneralError += (sender, e) =>
|
_client.OnGeneralError += (sender, e) =>
|
||||||
Console.WriteLine($"Error: {e.Message}");
|
Console.WriteLine($"[ERROR] {e.Message}");
|
||||||
|
|
||||||
// Subscribe to events
|
_client.OnSslError += (sender, e) =>
|
||||||
|
Console.WriteLine($"[SSL ERROR] {e.Message}");
|
||||||
|
|
||||||
|
// Connection events
|
||||||
_client.OnConnected += async (sender, e) =>
|
_client.OnConnected += async (sender, e) =>
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Connected to server at {e.RemoteEndPoint}");
|
Console.WriteLine($"[CONNECTED] Connected to server at {e.RemoteEndPoint}");
|
||||||
|
|
||||||
// Set nickname
|
// Set client nickname
|
||||||
await _client.SetNicknameAsync("TestUser");
|
await _client.SendNicknameAsync("TestUser");
|
||||||
|
|
||||||
// Send a message
|
// Send initial message
|
||||||
await _client.SendAsync("Hello server!");
|
await _client.SendAsync("Hello from client!");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Data reception
|
||||||
_client.OnDataReceived += (sender, e) =>
|
_client.OnDataReceived += (sender, e) =>
|
||||||
Console.WriteLine($"Server says: {(e.IsBinary ? $"{e.Data.Length} bytes" : e.StringData)}");
|
Console.WriteLine($"[SERVER] {(e.IsBinary ? $"{e.Data.Length} bytes" : e.StringData)}");
|
||||||
|
|
||||||
|
// Disconnection
|
||||||
_client.OnDisconnected += (sender, e) =>
|
_client.OnDisconnected += (sender, e) =>
|
||||||
{
|
Console.WriteLine($"[DISCONNECTED] Disconnected from server ({e.DisconnectReason})");
|
||||||
Console.WriteLine("Disconnected from server");
|
|
||||||
};
|
|
||||||
|
|
||||||
Console.WriteLine("Connecting to server...");
|
// Idle/Timeout
|
||||||
|
_client.OnIdleTimeout += (sender, e) =>
|
||||||
|
Console.WriteLine($"[IDLE] Connection idle for {e.IdleTime} seconds");
|
||||||
|
|
||||||
|
// Heartbeat
|
||||||
|
_client.OnPingResponse += (sender, e) =>
|
||||||
|
Console.WriteLine($"[PING] Response time: {e.ResponseTime}ms");
|
||||||
|
|
||||||
|
Console.WriteLine("Client connecting...");
|
||||||
await _client.ConnectAsync();
|
await _client.ConnectAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Advanced Usage Examples
|
||||||
|
|
||||||
|
### Request-Response Pattern
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Server: Wait for client to send data, then respond
|
||||||
|
_server.OnDataReceived += async (sender, e) =>
|
||||||
|
{
|
||||||
|
if (e.StringData == "QUERY")
|
||||||
|
{
|
||||||
|
await _server.SendToClientAsync(e.ClientId, "RESPONSE_DATA");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Client: Send request and wait for response
|
||||||
|
var response = await _client.SendAndWaitForResponseAsync(
|
||||||
|
"QUERY", TimeSpan.FromSeconds(10));
|
||||||
|
Console.WriteLine($"Got: {response.StringData}");
|
||||||
|
|
||||||
|
// Server: Broadcast and wait for all responses
|
||||||
|
var responses = await _server.BroadcastAndWaitForResponsesAsync(
|
||||||
|
"QUESTION", TimeSpan.FromSeconds(15));
|
||||||
|
Console.WriteLine($"Received {responses.Count} responses");
|
||||||
|
```
|
||||||
|
|
||||||
|
### Client-to-Client Communication
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Client A sends to Client B through server
|
||||||
|
await _server.SendFromClientToClientAsync(
|
||||||
|
"clientA_id", "clientB_id", "Direct message");
|
||||||
|
|
||||||
|
// Or with response
|
||||||
|
var response = await _server.SendFromClientToClientAndWaitForResponseAsync(
|
||||||
|
"clientA_id", "clientB_id", "Question for B");
|
||||||
|
```
|
||||||
|
|
||||||
|
### Message Framing
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Delimiter-based framing
|
||||||
|
config.MessageFraming = FramingMode.Delimiter;
|
||||||
|
config.Delimiter = Encoding.UTF8.GetBytes("\n");
|
||||||
|
|
||||||
|
// Length-prefixed framing
|
||||||
|
config.MessageFraming = FramingMode.LengthPrefixed;
|
||||||
|
config.LengthPrefixedLength = 4; // 4-byte length prefix
|
||||||
|
|
||||||
|
// Custom separator
|
||||||
|
server.SetSeparator("||");
|
||||||
|
```
|
||||||
|
|
||||||
|
### Accessing Statistics
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Server statistics
|
||||||
|
Console.WriteLine($"Active connections: {_server.ActiveConnections}");
|
||||||
|
Console.WriteLine($"Total connected: {_server.TotalConnections}");
|
||||||
|
Console.WriteLine($"Messages/sec: {_server.MessagesPerSecond}");
|
||||||
|
Console.WriteLine($"Data sent: {_server.BytesSent} bytes");
|
||||||
|
Console.WriteLine($"Data received: {_server.BytesReceived} bytes");
|
||||||
|
Console.WriteLine($"Uptime: {_server.Uptime}");
|
||||||
|
|
||||||
|
var stats = _server.GetStats();
|
||||||
|
Console.WriteLine($"Dropped packets: {stats.DroppedPackets}");
|
||||||
|
Console.WriteLine($"Dropped connections: {stats.DroppedConnections}");
|
||||||
|
|
||||||
|
// Client statistics
|
||||||
|
Console.WriteLine($"Connection time: {_client.ConnectedTimeInHours()} hours");
|
||||||
|
Console.WriteLine($"Idle time: {_client.IdleTimeInSeconds()} seconds");
|
||||||
|
Console.WriteLine($"Bytes sent: {_client.BytesSent}");
|
||||||
|
Console.WriteLine($"Messages sent: {_client.MessagesSent}");
|
||||||
|
```
|
||||||
|
|
||||||
|
### SSL/TLS with Certificate Validation
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Server with certificate
|
||||||
|
var config = new Configuration
|
||||||
|
{
|
||||||
|
Certificate = new X509Certificate2("server.pfx", "password"),
|
||||||
|
MutuallyAuthenticate = true,
|
||||||
|
CheckCertificateRevocation = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Client with certificate validation
|
||||||
|
var clientConfig = new Configuration
|
||||||
|
{
|
||||||
|
Host = "server.example.com",
|
||||||
|
Port = 443,
|
||||||
|
Certificate = new X509Certificate2("client.pfx", "password"),
|
||||||
|
MutuallyAuthenticate = true,
|
||||||
|
TrustedThumbprints = new List<string> { "CERT_THUMBPRINT" },
|
||||||
|
CheckCertificateRevocation = true,
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Encryption and Security
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// AES Encryption
|
||||||
|
config.UseAesEncryption = true;
|
||||||
|
config.AesPassword = "SecurePassword123";
|
||||||
|
|
||||||
|
// Both TLS and AES together
|
||||||
|
config.Certificate = new X509Certificate2("server.pfx", "pass");
|
||||||
|
config.UseAesEncryption = true;
|
||||||
|
config.AesPassword = "AdditionalPassword";
|
||||||
|
|
||||||
|
// Disable Nagle for low-latency
|
||||||
|
config.EnableNagle = false;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Event Arguments Reference
|
||||||
|
|
||||||
|
### ConnectionEventArgs
|
||||||
|
- `string ClientId` - Unique client identifier
|
||||||
|
- `EndPoint RemoteEndPoint` - Client's remote address/port
|
||||||
|
- `string Nickname` - Client's nickname (if set)
|
||||||
|
- `bool HasNickname` - Whether client has a nickname
|
||||||
|
|
||||||
|
### DataReceivedEventArgs
|
||||||
|
- `string ClientId` - Sender client ID
|
||||||
|
- `byte[] Data` - Raw binary data
|
||||||
|
- `string StringData` - Data decoded as UTF-8 string
|
||||||
|
- `bool IsBinary` - True if binary data, false if text
|
||||||
|
- `bool HasNickname` - Whether sender has a nickname
|
||||||
|
- `string Nickname` - Sender's nickname (if set)
|
||||||
|
|
||||||
|
### ErrorEventArgs
|
||||||
|
- `string Message` - Error description
|
||||||
|
- `string ClientId` - Associated client (if applicable)
|
||||||
|
- `Exception Exception` - Inner exception details
|
||||||
|
|
||||||
|
### PingEventArgs
|
||||||
|
- `string ClientId` - Client that responded to ping
|
||||||
|
- `long ResponseTime` - Response time in milliseconds
|
||||||
|
|
||||||
|
### IdleEventArgs / IdleClientEventArgs
|
||||||
|
- `string ClientId` - Idle client ID
|
||||||
|
- `double IdleTime` - Idle duration in seconds
|
||||||
|
- `DisconnectReason Reason` - Reason for disconnection
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Disconnect Reasons
|
||||||
|
|
||||||
|
The `DisconnectReason` enum provides detailed disconnect information:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public enum DisconnectReason
|
||||||
|
{
|
||||||
|
Unknown, // Unknown reason
|
||||||
|
RemoteClosed, // Remote peer closed connection
|
||||||
|
LocalClosed, // Local side closed connection
|
||||||
|
Timeout, // Connection timeout
|
||||||
|
Error, // General error
|
||||||
|
SSLError, // SSL/TLS handshake error
|
||||||
|
ServerShutdown, // Server shutting down
|
||||||
|
Reconnect, // Reconnecting (client)
|
||||||
|
ClientRequested, // Client requested disconnect
|
||||||
|
Forced, // Forcefully disconnected
|
||||||
|
NoPongReceived, // Heartbeat pong not received
|
||||||
|
ProtocolError, // Protocol violation
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Performance Considerations
|
||||||
|
|
||||||
|
### Optimization Tips
|
||||||
|
1. **Disable Nagle's Algorithm**: Set `EnableNagle = false` for lower latency
|
||||||
|
2. **Buffer Size**: Use appropriate `BufferSize` (default is ExtraLarge ~65KB)
|
||||||
|
3. **Keep-Alive**: Enable `EnableKeepAlive` to detect dead connections early
|
||||||
|
4. **Message Framing**: Use `LengthPrefixed` for better parsing performance
|
||||||
|
5. **Connection Pool**: Design clients to maintain persistent connections
|
||||||
|
6. **Batching**: Group small messages when possible
|
||||||
|
7. **Thread Affinity**: Run on dedicated cores for high throughput
|
||||||
|
|
||||||
|
### Monitoring
|
||||||
|
- Use `HealthApi` for external monitoring
|
||||||
|
- Enable `EnableServerStatusPage` for HTML reports
|
||||||
|
- Monitor `MessagesPerSecond` for throughput
|
||||||
|
- Track `IdleTimeout` events for connection health
|
||||||
|
- Use `NetworkMonitor` for advanced diagnostics
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Thread Safety
|
||||||
|
|
||||||
|
- `NetworkServer` and `NetworkClient` are thread-safe for all public methods
|
||||||
|
- Events are called on socket completion threads
|
||||||
|
- Use `ConfigureAwait(false)` in async handlers
|
||||||
|
- Access shared state with appropriate synchronization
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Supported Platforms
|
||||||
|
|
||||||
|
- **.NET Framework 4.8+** - Full support
|
||||||
|
- **.NET 8.0** - Full support
|
||||||
|
- **Platforms**: Windows, Linux, macOS (with .NET Core)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Licensed under the Apache License. See LICENSE file for details.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- **Repository**: https://git.saey.me/EonaCat/EonaCat.Connections
|
||||||
|
- **Issues**: Report bugs and feature requests on the project repository
|
||||||
|
- **Documentation**: Inline XML documentation in source code
|
||||||
|
|||||||
Reference in New Issue
Block a user