using System; using System.Net.Sockets; using System.Threading; using TcpClient = NetCoreServer.TcpClient; using com.chronoxor.simple; using com.chronoxor.simple.FBE; namespace ProtoClient { public class TcpProtoClient : TcpClient { public TcpProtoClient(string address, int port) : base(address, port) {} public bool ConnectAndStart() { Console.WriteLine($"TCP protocol client starting a new session with Id '{Id}'..."); StartReconnectTimer(); return ConnectAsync(); } public bool DisconnectAndStop() { Console.WriteLine($"TCP protocol client stopping the session with Id '{Id}'..."); StopReconnectTimer(); DisconnectAsync(); return true; } public override bool Reconnect() { return ReconnectAsync(); } private Timer _reconnectTimer; public void StartReconnectTimer() { // Start the reconnect timer _reconnectTimer = new Timer(state => { Console.WriteLine($"TCP reconnect timer connecting the client session with Id '{Id}'..."); ConnectAsync(); }, null, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan); } public void StopReconnectTimer() { // Stop the reconnect timer _reconnectTimer?.Dispose(); _reconnectTimer = null; } public delegate void ConnectedHandler(); public event ConnectedHandler Connected = () => {}; protected override void OnConnected() { Console.WriteLine($"TCP protocol client connected a new session with Id '{Id}' to remote address '{Address}' and port {Port}"); Connected?.Invoke(); } public delegate void DisconnectedHandler(); public event DisconnectedHandler Disconnected = () => {}; protected override void OnDisconnected() { Console.WriteLine($"TCP protocol client disconnected the session with Id '{Id}'"); // Setup and asynchronously wait for the reconnect timer _reconnectTimer?.Change(TimeSpan.FromSeconds(1), Timeout.InfiniteTimeSpan); Disconnected?.Invoke(); } public delegate void ReceivedHandler(byte[] buffer, long offset, long size); public event ReceivedHandler Received = (buffer, offset, size) => {}; protected override void OnReceived(byte[] buffer, long offset, long size) { Received?.Invoke(buffer, offset, size); } protected override void OnError(SocketError error) { Console.WriteLine($"TCP protocol client caught a socket error: {error}"); } #region IDisposable implementation // Disposed flag. private bool _disposed; protected override void Dispose(bool disposingManagedResources) { if (!_disposed) { if (disposingManagedResources) { // Dispose managed resources here... StopReconnectTimer(); } // Dispose unmanaged resources here... // Set large fields to null here... // Mark as disposed. _disposed = true; } // Call Dispose in the base class. base.Dispose(disposingManagedResources); } // The derived class does not have a Finalize method // or a Dispose method without parameters because it inherits // them from the base class. #endregion } public class SimpleProtoClient : Client, ISenderListener, IReceiverListener, IDisposable { private readonly TcpProtoClient _tcpProtoClient; public Guid Id => _tcpProtoClient.Id; public bool IsConnected => _tcpProtoClient.IsConnected; public SimpleProtoClient(string address, int port) { _tcpProtoClient = new TcpProtoClient(address, port); _tcpProtoClient.Connected += OnConnected; _tcpProtoClient.Disconnected += OnDisconnected; _tcpProtoClient.Received += OnReceived; ReceivedResponse_DisconnectRequest += HandleDisconnectRequest; ReceivedResponse_SimpleResponse += HandleSimpleResponse; ReceivedResponse_SimpleReject += HandleSimpleReject; ReceivedResponse_SimpleNotify += HandleSimpleNotify; } private void DisposeClient() { _tcpProtoClient.Connected -= OnConnected; _tcpProtoClient.Connected -= OnDisconnected; _tcpProtoClient.Received -= OnReceived; ReceivedResponse_DisconnectRequest -= HandleDisconnectRequest; ReceivedResponse_SimpleResponse -= HandleSimpleResponse; ReceivedResponse_SimpleReject -= HandleSimpleReject; ReceivedResponse_SimpleNotify -= HandleSimpleNotify; _tcpProtoClient.Dispose(); } public bool ConnectAndStart() { return _tcpProtoClient.ConnectAndStart(); } public bool DisconnectAndStop() { return _tcpProtoClient.DisconnectAndStop(); } public bool Reconnect() { return _tcpProtoClient.Reconnect(); } private bool _watchdog; private Thread _watchdogThread; public bool StartWatchdog() { if (_watchdog) return false; Console.WriteLine("Watchdog thread starting..."); // Start the watchdog thread _watchdog = true; _watchdogThread = new Thread(WatchdogThread); Console.WriteLine("Watchdog thread started!"); return true; } public bool StopWatchdog() { if (!_watchdog) return false; Console.WriteLine("Watchdog thread stopping..."); // Stop the watchdog thread _watchdog = false; _watchdogThread.Join(); Console.WriteLine("Watchdog thread stopped!"); return true; } public static void WatchdogThread(object obj) { var instance = obj as SimpleProtoClient; if (instance == null) return; try { // Watchdog loop... while (instance._watchdog) { var utc = DateTime.UtcNow; // Watchdog the client instance.Watchdog(utc); // Sleep for a while... Thread.Sleep(1000); } } catch (Exception e) { Console.WriteLine($"Config client watchdog thread terminated: {e}"); } } #region Connection handlers public delegate void ConnectedHandler(); public event ConnectedHandler Connected = () => {}; private void OnConnected() { // Reset FBE protocol buffers Reset(); Connected?.Invoke(); } public delegate void DisconnectedHandler(); public event DisconnectedHandler Disconnected = () => {}; private void OnDisconnected() { Disconnected?.Invoke(); } public long OnSend(byte[] buffer, long offset, long size) { return _tcpProtoClient.SendAsync(buffer, offset, size) ? size : 0; } public void OnReceived(byte[] buffer, long offset, long size) { Receive(buffer, offset, size); } #endregion #region Protocol handlers private void HandleDisconnectRequest(DisconnectRequest request) { Console.WriteLine($"Received: {request}"); _tcpProtoClient.DisconnectAsync(); } private void HandleSimpleResponse(SimpleResponse response) { Console.WriteLine($"Received: {response}"); } private void HandleSimpleReject(SimpleReject reject) { Console.WriteLine($"Received: {reject}"); } private void HandleSimpleNotify(SimpleNotify notify) { Console.WriteLine($"Received: {notify}"); } #endregion #region IDisposable implementation // Disposed flag. private bool _disposed; // Implement IDisposable. public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposingManagedResources) { // The idea here is that Dispose(Boolean) knows whether it is // being called to do explicit cleanup (the Boolean is true) // versus being called due to a garbage collection (the Boolean // is false). This distinction is useful because, when being // disposed explicitly, the Dispose(Boolean) method can safely // execute code using reference type fields that refer to other // objects knowing for sure that these other objects have not been // finalized or disposed of yet. When the Boolean is false, // the Dispose(Boolean) method should not execute code that // refer to reference type fields because those objects may // have already been finalized." if (!_disposed) { if (disposingManagedResources) { // Dispose managed resources here... DisposeClient(); } // Dispose unmanaged resources here... // Set large fields to null here... // Mark as disposed. _disposed = true; } } #endregion } class Program { static void Main(string[] args) { // Simple protocol server address string address = "127.0.0.1"; if (args.Length > 0) address = args[0]; // Simple protocol server port int port = 4444; if (args.Length > 1) port = int.Parse(args[1]); Console.WriteLine($"Simple protocol server address: {address}"); Console.WriteLine($"Simple protocol server port: {port}"); Console.WriteLine(); // Create a new simple protocol chat client var client = new SimpleProtoClient(address, port); // Connect the client Console.Write("Client connecting..."); client.ConnectAndStart(); Console.WriteLine("Done!"); client.StartWatchdog(); Console.WriteLine("Press Enter to stop the client or '!' to reconnect the client..."); // Perform text input for (;;) { string line = Console.ReadLine(); if (string.IsNullOrEmpty(line)) break; // Disconnect the client if (line == "!") { Console.Write("Client disconnecting..."); client.Reconnect(); Console.WriteLine("Done!"); continue; } // Send request to the simple protocol server SimpleRequest request = SimpleRequest.Default; request.Message = line; var response = client.Request(request).Result; // Show string hash calculation result Console.WriteLine($"Hash of '{line}' = 0x{response.Hash:X8}"); } client.StopWatchdog(); // Disconnect the client Console.Write("Client disconnecting..."); client.DisconnectAndStop(); Console.WriteLine("Done!"); } } }