private async Task SendHeadersAsync(WatsonMessage msg, CancellationToken token) { byte[] headerBytes = msg.HeaderBytes; await _DataStream.WriteAsync(headerBytes, 0, headerBytes.Length, token).ConfigureAwait(false); await _DataStream.FlushAsync(token).ConfigureAwait(false); }
internal async Task <WatsonMessage> MessageReadAsync(bool readDataStream) { WatsonMessage msg = new WatsonMessage(TrafficStream); await msg.Build(readDataStream); return(msg); }
/// <summary> /// Disconnects the specified client. /// </summary> /// <param name="ipPort">IP:port of the client.</param> public void DisconnectClient(string ipPort) { if (String.IsNullOrEmpty(ipPort)) { throw new ArgumentNullException(nameof(ipPort)); } if (!_Clients.TryGetValue(ipPort, out ClientMetadata client)) { Log("*** DisconnectClient unable to find client " + ipPort); } else { byte[] data = null; if (_ClientsTimedout.ContainsKey(ipPort)) { data = Encoding.UTF8.GetBytes("Removed from server due to timeout."); } else { data = Encoding.UTF8.GetBytes("Removed from server."); _ClientsKicked.TryAdd(ipPort, DateTime.Now); } WatsonMessage removeMsg = new WatsonMessage(); removeMsg.Status = MessageStatus.Removed; removeMsg.Data = null; removeMsg.ContentLength = 0; MessageWrite(client, removeMsg, null); client.Dispose(); _Clients.TryRemove(ipPort, out ClientMetadata removed); } }
private async Task <WatsonMessage> MessageReadAsync(ClientMetadata client) { /* * * Do not catch exceptions, let them get caught by the data reader * to destroy the connection * */ WatsonMessage msg = null; if (_Mode == Mode.Ssl) { msg = new WatsonMessage(client.SslStream, Debug); await msg.Build(); } else if (_Mode == Mode.Tcp) { msg = new WatsonMessage(client.NetworkStream, Debug); await msg.Build(); } else { throw new ArgumentException("Unknown mode: " + _Mode.ToString()); } return(msg); }
private bool MessageWrite(ClientMetadata client, WatsonMessage msg, byte[] data) { if (client == null) { throw new ArgumentNullException(nameof(client)); } if (msg == null) { throw new ArgumentNullException(nameof(msg)); } int dataLen = 0; if (data != null) { dataLen = data.Length; } byte[] headerBytes = msg.ToHeaderBytes(dataLen); _SendLock.Wait(); try { if (_Mode == Mode.Tcp) { client.NetworkStream.Write(headerBytes, 0, headerBytes.Length); if (dataLen > 0) { client.NetworkStream.Write(data, 0, dataLen); } client.NetworkStream.Flush(); } else if (_Mode == Mode.Ssl) { client.SslStream.Write(headerBytes, 0, headerBytes.Length); if (dataLen > 0) { client.SslStream.Write(data, 0, dataLen); } client.SslStream.Flush(); } else { throw new ArgumentException("Unknown mode: " + _Mode.ToString()); } return(true); } catch (Exception) { Log("*** MessageWrite " + client.IpPort + " disconnected due to exception"); return(false); } finally { _SendLock.Release(); } }
/// <summary> /// Tear down the client and dispose of background workers. /// </summary> public void Dispose() { Log("Disposing WatsonTcpClient"); if (Connected) { WatsonMessage msg = new WatsonMessage(); msg.Status = MessageStatus.Disconnecting; msg.Data = null; msg.ContentLength = 0; MessageWrite(msg); } if (_TokenSource != null) { if (!_TokenSource.IsCancellationRequested) { _TokenSource.Cancel(); } _TokenSource.Dispose(); _TokenSource = null; } if (_WriteLock != null) { _WriteLock.Dispose(); _WriteLock = null; } if (_ReadLock != null) { _ReadLock.Dispose(); _ReadLock = null; } if (_SslStream != null) { _SslStream.Close(); _SslStream.Dispose(); _SslStream = null; } if (_TcpStream != null) { _TcpStream.Close(); _TcpStream.Dispose(); _TcpStream = null; } if (_Client != null) { _Client.Close(); _Client.Dispose(); _Client = null; } Connected = false; Log("Dispose routine complete"); }
internal async Task <bool> MessageWriteAsync(WatsonMessage msg, int readStreamBufferSize) { if (msg == null) { throw new ArgumentNullException(nameof(msg)); } if (msg.ContentLength > 0) { if (msg.DataStream == null || !msg.DataStream.CanRead) { throw new ArgumentException("Cannot read from supplied stream."); } } byte[] headerBytes = msg.ToHeaderBytes(msg.ContentLength); int bytesRead; long bytesRemaining = msg.ContentLength; byte[] buffer = new byte[readStreamBufferSize]; try { await _WriteLock.WaitAsync(); await _TrafficStream.WriteAsync(headerBytes, 0, headerBytes.Length); if (msg.ContentLength > 0) { while (bytesRemaining > 0) { bytesRead = await msg.DataStream.ReadAsync(buffer, 0, buffer.Length); if (bytesRead > 0) { await _TrafficStream.WriteAsync(buffer, 0, bytesRead); bytesRemaining -= bytesRead; } } } await _TrafficStream.FlushAsync(); Common.Log($"MessageWriteAsync sent {Encoding.UTF8.GetString(headerBytes)}"); return(true); } catch (Exception e) { Common.Log($"*** MessageWriteAsync {_IpPort} disconnected due to exception: {e.Message}"); return(false); } finally { _WriteLock.Release(); } }
/// <summary> /// Send data to the specified client, asynchronously. /// </summary> /// <param name="ipPort">IP:port of the recipient client.</param> /// <param name="msg">Populated message object.</param> /// <returns>Task with Boolean indicating if the message was sent successfully.</returns> public async Task <bool> SendAsync(string ipPort, WatsonMessage msg) { if (!_Clients.TryGetValue(ipPort, out ClientMetadata client)) { Log("*** SendAsync unable to find client " + ipPort); return(false); } return(await MessageWriteAsync(client, msg)); }
/// <summary> /// Send data to the specified client. /// </summary> /// <param name="ipPort">IP:port of the recipient client.</param> /// <param name="msg">Populated message object.</param> /// <returns>Boolean indicating if the message was sent successfully.</returns> public bool Send(string ipPort, WatsonMessage msg) { if (!_Clients.TryGetValue(ipPort, out ClientMetadata client)) { Log("*** Send unable to find client " + ipPort); return(false); } return(MessageWrite(client, msg)); }
internal bool MessageWrite(WatsonMessage msg, int readStreamBufferSize) { if (msg == null) { throw new ArgumentNullException(nameof(msg)); } if (msg.ContentLength > 0) { if (msg.DataStream == null || !msg.DataStream.CanRead) { throw new ArgumentException("Cannot read from supplied stream."); } } byte[] headerBytes = msg.ToHeaderBytes(msg.ContentLength); int bytesRead; long bytesRemaining = msg.ContentLength; byte[] buffer = new byte[readStreamBufferSize]; try { _WriteLock.Wait(1); _TrafficStream.Write(headerBytes, 0, headerBytes.Length); if (msg.ContentLength > 0) { while (bytesRemaining > 0) { bytesRead = msg.DataStream.Read(buffer, 0, buffer.Length); if (bytesRead > 0) { _TrafficStream.Write(buffer, 0, bytesRead); bytesRemaining -= bytesRead; } } } _TrafficStream.Flush(); return(true); } catch (Exception e) { Common.Log($"*** MessageWrite {_IpPort} disconnected due to exception: {e.Message}"); return(false); } finally { _WriteLock.Release(); } }
internal static byte[] ReadMessageDataAsync(WatsonMessage msg, int bufferLen) { if (msg == null) { throw new ArgumentNullException(nameof(msg)); } if (msg.ContentLength == 0) { return(new byte[0]); } return(WatsonCommon.ReadFromStreamAsync(msg.DataStream, msg.ContentLength, bufferLen)); }
/// <summary> /// Send data to the specified client. /// </summary> /// <param name="ipPort">IP:port of the recipient client.</param> /// <param name="data">Byte array containing data.</param> /// <returns>Boolean indicating if the message was sent successfully.</returns> public bool Send(string ipPort, byte[] data) { if (!_Clients.TryGetValue(ipPort, out ClientMetadata client)) { Log("*** Send unable to find client " + ipPort); return(false); } WatsonMessage msg = new WatsonMessage(data, Debug); return(MessageWrite(client, msg, data)); }
/// <summary> /// Send data to the specified client using a stream, asynchronously. /// </summary> /// <param name="ipPort">IP:port of the recipient client.</param> /// <param name="contentLength">The number of bytes in the stream.</param> /// <param name="stream">The stream containing the data.</param> /// <returns>Task with Boolean indicating if the message was sent successfully.</returns> public async Task <bool> SendAsync(string ipPort, long contentLength, Stream stream) { if (!_Clients.TryGetValue(ipPort, out ClientMetadata client)) { Log("*** SendAsync unable to find client " + ipPort); return(false); } WatsonMessage msg = new WatsonMessage(contentLength, stream, Debug); return(await MessageWriteAsync(client, msg, contentLength, stream)); }
private async Task <bool> MessageWriteAsync(ClientMetadata client, WatsonMessage msg, byte[] data) { int dataLen = 0; MemoryStream ms = new MemoryStream(); if (data != null && data.Length > 0) { dataLen = data.Length; ms.Write(data, 0, data.Length); ms.Seek(0, SeekOrigin.Begin); } return(await MessageWriteAsync(client, msg, dataLen, ms)); }
internal async Task <bool> MessageWriteAsync(MessageStatus messageStatus, byte[] data, int readStreamBufferSize) { using (MemoryStream ms = new MemoryStream()) { if (data != null && data.Length > 0) { ms.Write(data, 0, data.Length); ms.Seek(0, SeekOrigin.Begin); } WatsonMessage msg = new WatsonMessage(messageStatus, data.Length, ms); return(await MessageWriteAsync(msg, readStreamBufferSize)); } }
/// <summary> /// Send data and metadata to the server using a stream. /// </summary> /// <param name="contentLength">The number of bytes in the stream.</param> /// <param name="stream">The stream containing the data.</param> /// <param name="metadata">Dictionary containing metadata.</param> /// <returns>Boolean indicating if the message was sent successfully.</returns> public bool Send(long contentLength, Stream stream, Dictionary <object, object> metadata = null) { if (contentLength < 0) { throw new ArgumentException("Content length must be zero or greater."); } if (stream == null) { stream = new MemoryStream(new byte[0]); } WatsonMessage msg = new WatsonMessage(metadata, contentLength, stream, false, false, null, null, (_Settings.DebugMessages ? _Settings.Logger : null)); return(SendInternal(msg, contentLength, stream)); }
/// <summary> /// Send data to the specified client, asynchronously. /// </summary> /// <param name="ipPort">IP:port of the recipient client.</param> /// <param name="data">Byte array containing data.</param> /// <returns>Task with Boolean indicating if the message was sent successfully.</returns> public async Task <bool> SendAsync(string ipPort, byte[] data) { if (String.IsNullOrEmpty(ipPort)) { throw new ArgumentNullException(nameof(ipPort)); } if (!_Clients.TryGetValue(ipPort, out ClientMetadata client)) { Log("*** SendAsync unable to find client " + ipPort); return(false); } WatsonMessage msg = new WatsonMessage(data, Debug); return(await MessageWriteAsync(client, msg, data)); }
/// <summary> /// Send data to the specified client using a stream. /// </summary> /// <param name="ipPort">IP:port of the recipient client.</param> /// <param name="contentLength">The number of bytes in the stream.</param> /// <param name="stream">The stream containing the data.</param> /// <returns>Boolean indicating if the message was sent successfully.</returns> public bool Send(string ipPort, long contentLength, Stream stream) { if (String.IsNullOrEmpty(ipPort)) { throw new ArgumentNullException(nameof(ipPort)); } if (!_Clients.TryGetValue(ipPort, out ClientMetadata client)) { Log("*** Send unable to find client " + ipPort); return(false); } WatsonMessage msg = new WatsonMessage(contentLength, stream, Debug); return(MessageWrite(client, msg, contentLength, stream)); }
private async Task <bool> MessageWriteAsync(ClientMetadata client, WatsonMessage msg) { if (client == null) { throw new ArgumentNullException(nameof(client)); } if (msg == null) { throw new ArgumentNullException(nameof(msg)); } byte[] msgBytes = msg.ToBytes(); await _SendLock.WaitAsync(); try { if (_Mode == Mode.Tcp) { await client.NetworkStream.WriteAsync(msgBytes, 0, msgBytes.Length); await client.NetworkStream.FlushAsync(); } else if (_Mode == Mode.Ssl) { await client.SslStream.WriteAsync(msgBytes, 0, msgBytes.Length); await client.SslStream.FlushAsync(); } else { throw new ArgumentException("Unknown mode: " + _Mode.ToString()); } return(true); } catch (Exception) { Log("*** MessageWriteAsync " + client.IpPort + " disconnected due to exception"); return(false); } finally { _SendLock.Release(); } }
/// <summary> /// Send a pre-shared key to the server to authenticate. /// </summary> /// <param name="presharedKey">Up to 16-character string.</param> public void Authenticate(string presharedKey) { if (String.IsNullOrEmpty(presharedKey)) { throw new ArgumentNullException(nameof(presharedKey)); } if (presharedKey.Length != 16) { throw new ArgumentException("Preshared key length must be 16 bytes."); } WatsonMessage msg = new WatsonMessage(); msg.Status = MessageStatus.AuthRequested; msg.PresharedKey = Encoding.UTF8.GetBytes(presharedKey); SendInternal(msg, 0, null); }
private void FinalizeConnection(ClientMetadata client) { #region Add-to-Client-List if (!AddClient(client)) { Log("*** FinalizeConnection unable to add client " + client.IpPort); client.Dispose(); return; } // Do not decrement in this block, decrement is done by the connection reader int activeCount = Interlocked.Increment(ref _ActiveClients); #endregion #region Request-Authentication if (!String.IsNullOrEmpty(PresharedKey)) { Log("*** FinalizeConnection soliciting authentication material from " + client.IpPort); _UnauthenticatedClients.TryAdd(client.IpPort, DateTime.Now); byte[] data = Encoding.UTF8.GetBytes("Authentication required"); WatsonMessage authMsg = new WatsonMessage(); authMsg.Status = MessageStatus.AuthRequired; authMsg.Data = null; authMsg.ContentLength = 0; MessageWrite(client, authMsg, null); } #endregion #region Start-Data-Receiver Log("*** FinalizeConnection starting data receiver for " + client.IpPort + " (now " + activeCount + " clients)"); if (ClientConnected != null) { Task.Run(() => ClientConnected(client.IpPort)); } Task.Run(async() => await DataReceiver(client)); #endregion }
/// <summary> /// Disconnect from the server. /// </summary> public void Disconnect() { if (!Connected) { throw new InvalidOperationException("Nonnected to the server."); } _Settings.Logger?.Invoke(_Header + "disconnecting"); if (Connected) { WatsonMessage msg = new WatsonMessage(); msg.Status = MessageStatus.Shutdown; SendInternal(msg, 0, null); } if (_TokenSource != null) { // stop the data receiver if (!_TokenSource.IsCancellationRequested) { _TokenSource.Cancel(); _TokenSource.Dispose(); } } if (_SslStream != null) { _SslStream.Close(); } if (_TcpStream != null) { _TcpStream.Close(); } if (_Client != null) { _Client.Close(); } Connected = false; _Settings.Logger?.Invoke(_Header + "disconnected"); }
private bool MessageWrite(ClientMetadata client, WatsonMessage msg, byte[] data) { int dataLen = 0; MemoryStream ms = new MemoryStream(); if (data != null && data.Length > 0) { dataLen = data.Length; ms.Write(data, 0, data.Length); ms.Seek(0, SeekOrigin.Begin); } else { ms = new MemoryStream(new byte[0]); } return(MessageWrite(client, msg, dataLen, ms)); }
/// <summary> /// Send a pre-shared key to the server to authenticate. /// </summary> /// <param name="presharedKey">Up to 16-character string.</param> public void Authenticate(string presharedKey) { if (String.IsNullOrEmpty(presharedKey)) { throw new ArgumentNullException(nameof(presharedKey)); } if (presharedKey.Length > 16) { throw new ArgumentException("Preshared key length must be 16 or fewer characters"); } presharedKey = presharedKey.PadRight(16, ' '); WatsonMessage msg = new WatsonMessage(Encoding.UTF8.GetBytes(presharedKey), Debug); msg.Status = MessageStatus.AuthRequired; msg.PresharedKey = Encoding.UTF8.GetBytes(presharedKey); Send(msg); }
/// <summary> /// Send data and metadata to the server from a stream asynchronously. /// </summary> /// <param name="contentLength">The number of bytes to send.</param> /// <param name="stream">The stream containing the data.</param> /// <param name="metadata">Dictionary containing metadata.</param> /// <param name="token">Cancellation token to cancel the request.</param> /// <returns>Task with Boolean indicating if the message was sent successfully.</returns> public async Task <bool> SendAsync(long contentLength, Stream stream, Dictionary <object, object> metadata = null, CancellationToken token = default) { if (contentLength < 0) { throw new ArgumentException("Content length must be zero or greater."); } if (token == default(CancellationToken)) { token = _Token; } if (stream == null) { stream = new MemoryStream(new byte[0]); } WatsonMessage msg = new WatsonMessage(metadata, contentLength, stream, false, false, null, null, (_Settings.DebugMessages ? _Settings.Logger : null)); return(await SendInternalAsync(msg, contentLength, stream, token).ConfigureAwait(false)); }
private void FinalizeConnection(ClientMetadata client) { #region Add-to-Client-List ClientMetadata removed = null; DateTime ts; _Clients.TryRemove(client.IpPort, out removed); _ClientsLastSeen.TryRemove(client.IpPort, out ts); _Clients.TryAdd(client.IpPort, client); _ClientsLastSeen.TryAdd(client.IpPort, DateTime.Now); #endregion Add-to-Client-List #region Request-Authentication if (!String.IsNullOrEmpty(PresharedKey)) { Log("*** FinalizeConnection soliciting authentication material from " + client.IpPort); _UnauthenticatedClients.TryAdd(client.IpPort, DateTime.Now); byte[] data = Encoding.UTF8.GetBytes("Authentication required"); WatsonMessage authMsg = new WatsonMessage(); authMsg.Status = MessageStatus.AuthRequired; authMsg.Data = null; authMsg.ContentLength = 0; MessageWrite(client, authMsg, null); } #endregion Request-Authentication #region Start-Data-Receiver Log("*** FinalizeConnection starting data receiver for " + client.IpPort); if (ClientConnected != null) { Task.Run(() => ClientConnected(client.IpPort)); } Task.Run(async() => await DataReceiver(client, client.Token)); #endregion Start-Data-Receiver }
/// <summary> /// Send data and wait for a response for the specified number of milliseconds. A TimeoutException will be thrown if a response is not received. /// </summary> /// <param name="timeoutMs">Number of milliseconds to wait before considering a request to be expired.</param> /// <param name="contentLength">The number of bytes to send from the supplied stream.</param> /// <param name="stream">Stream containing data.</param> /// <param name="metadata">Metadata dictionary to attach to the message.</param> /// <returns>SyncResponse.</returns> public SyncResponse SendAndWait(int timeoutMs, long contentLength, Stream stream, Dictionary <object, object> metadata = null) { if (contentLength < 0) { throw new ArgumentException("Content length must be zero or greater."); } if (timeoutMs < 1000) { throw new ArgumentException("Timeout milliseconds must be 1000 or greater."); } if (stream == null) { stream = new MemoryStream(new byte[0]); } DateTime expiration = DateTime.Now.AddMilliseconds(timeoutMs); WatsonMessage msg = new WatsonMessage(metadata, contentLength, stream, true, false, expiration, Guid.NewGuid().ToString(), (_Settings.DebugMessages ? _Settings.Logger : null)); return(SendAndWaitInternal(msg, timeoutMs, contentLength, stream)); }
/// <summary> /// Send a pre-shared key to the server to authenticate. /// </summary> /// <param name="presharedKey">Up to 16-character string.</param> public void Authenticate(string presharedKey) { if (String.IsNullOrEmpty(presharedKey)) { throw new ArgumentNullException(nameof(presharedKey)); } if (presharedKey.Length != 16) { throw new ArgumentException("Preshared key length must be 16 bytes."); } presharedKey = presharedKey.PadRight(16, ' '); WatsonMessage msg = new WatsonMessage(); msg.Status = MessageStatus.AuthRequested; msg.PresharedKey = Encoding.UTF8.GetBytes(presharedKey); msg.Data = null; msg.ContentLength = 0; MessageWrite(msg); }
internal static DateTime GetExpirationTimestamp(WatsonMessage msg) { DateTime expiration = msg.Expiration.Value; if (msg.SenderTimestamp != null) { // // TimeSpan will be negative if sender timestamp is earlier than now or positive if sender timestamp is later than now // Goal #1: if sender has a later timestamp, decrease expiration by the difference between sender time and our time // Goal #2: if sender has an earlier timestamp, increase expiration by the difference between sender time and our time // // E.g. If sender time is 10:40 and receiver time is 10:45 and expiration is 1 minute, so 10:41. // ts = 10:45 - 10:40 = 5 minutes // expiration = 10:41 + 5 = 10:46 which is 1 minute later than when receiver received the message // TimeSpan ts = DateTime.Now - msg.SenderTimestamp.Value; expiration = expiration.AddMilliseconds(ts.TotalMilliseconds); } return(expiration); }
internal static async Task <byte[]> ReadMessageDataAsync(WatsonMessage msg, int bufferLen) { if (msg == null) { throw new ArgumentNullException(nameof(msg)); } if (msg.ContentLength == 0) { return(new byte[0]); } byte[] msgData = null; MemoryStream ms = new MemoryStream(); if (msg.Compression == CompressionType.None) { msgData = await WatsonCommon.ReadFromStreamAsync(msg.DataStream, msg.ContentLength, bufferLen); } else if (msg.Compression == CompressionType.Deflate) { using (DeflateStream ds = new DeflateStream(msg.DataStream, CompressionMode.Decompress, true)) { msgData = WatsonCommon.ReadStreamFully(ds); } } else if (msg.Compression == CompressionType.Gzip) { using (GZipStream gs = new GZipStream(msg.DataStream, CompressionMode.Decompress, true)) { msgData = WatsonCommon.ReadStreamFully(gs); } } else { throw new InvalidOperationException("Unknown compression type: " + msg.Compression.ToString()); } return(msgData); }