/// <summary> /// Stops TCP server and frees resources associated with it. /// </summary> public void Stop() { log.Info("()"); ShutdownSignaling.SignalShutdown(); try { Listener.Stop(); if ((clientQueueHandlerThread != null) && !clientQueueHandlerThreadFinished.WaitOne(10000)) { log.Error("Client queue handler thread did not terminated in 10 seconds."); } if ((acceptThread != null) && !acceptThreadFinished.WaitOne(10000)) { log.Error("Accept thread did not terminated in 10 seconds."); } lock (clientQueueLock) { log.Info("Closing {0} clients from new clients queue.", clientQueue.Count); while (clientQueue.Count > 0) { TcpClient client = clientQueue.Dequeue(); NetworkStream stream = client.GetStream(); if (stream != null) { stream.Dispose(); } client.Dispose(); } } } catch (Exception e) { log.Error("Exception occurred: {0}", e.ToString()); } log.Info("(-)"); }
/// <summary> /// Reads messages from the client stream and processes them in a loop until the client disconnects /// or until an action (such as a protocol violation) that leads to disconnecting of the client occurs. /// </summary> public async Task ReceiveMessageLoop() { log.Trace("()"); try { if (UseTls) { SslStream sslStream = (SslStream)Stream; await sslStream.AuthenticateAsServerAsync(Base.Configuration.TcpServerTlsCertificate, false, SslProtocols.Tls12, false); } Stream clientStream = Stream; byte[] messageHeaderBuffer = new byte[ProtocolHelper.HeaderSize]; byte[] messageBuffer = null; ClientStatus clientStatus = ClientStatus.ReadingHeader; uint messageSize = 0; int messageHeaderBytesRead = 0; int messageBytesRead = 0; while (!server.ShutdownSignaling.IsShutdown) { Task <int> readTask = null; int remain = 0; log.Trace("Client status is '{0}'.", clientStatus); switch (clientStatus) { case ClientStatus.ReadingHeader: { remain = ProtocolHelper.HeaderSize - messageHeaderBytesRead; readTask = clientStream.ReadAsync(messageHeaderBuffer, messageHeaderBytesRead, remain, server.ShutdownSignaling.ShutdownCancellationTokenSource.Token); break; } case ClientStatus.ReadingBody: { remain = (int)messageSize - messageBytesRead; readTask = clientStream.ReadAsync(messageBuffer, ProtocolHelper.HeaderSize + messageBytesRead, remain, server.ShutdownSignaling.ShutdownCancellationTokenSource.Token); break; } default: log.Error("Invalid client status '{0}'.", clientStatus); break; } if (readTask == null) { break; } log.Trace("{0} bytes remains to be read.", remain); int readAmount = await readTask; if (readAmount == 0) { log.Info("Connection has been closed."); break; } log.Trace("Read completed: {0} bytes.", readAmount); bool protoViolationDisconnect = false; bool disconnect = false; switch (clientStatus) { case ClientStatus.ReadingHeader: { messageHeaderBytesRead += readAmount; if (readAmount == remain) { if (messageHeaderBuffer[0] == 0x0D) { uint hdr = ProtocolHelper.GetValueLittleEndian(messageHeaderBuffer, 1); if (hdr + ProtocolHelper.HeaderSize <= ProtocolHelper.MaxSize) { messageSize = hdr; clientStatus = ClientStatus.ReadingBody; messageBuffer = new byte[ProtocolHelper.HeaderSize + messageSize]; Array.Copy(messageHeaderBuffer, messageBuffer, messageHeaderBuffer.Length); log.Trace("Reading of message header completed. Message size is {0} bytes.", messageSize); } else { log.Warn("Client claimed message of size {0} which exceeds the maximum.", hdr + ProtocolHelper.HeaderSize); protoViolationDisconnect = true; } } else { log.Warn("Message has invalid format - it's first byte is 0x{0:X2}, should be 0x0D.", messageHeaderBuffer[0]); protoViolationDisconnect = true; } } break; } case ClientStatus.ReadingBody: { messageBytesRead += readAmount; if (readAmount == remain) { clientStatus = ClientStatus.ReadingHeader; messageBytesRead = 0; messageHeaderBytesRead = 0; log.Trace("Reading of message size {0} completed.", messageSize); Message incomingMessage = CreateMessageFromRawData(messageBuffer); if (incomingMessage != null) { disconnect = !await messageProcessor.ProcessMessageAsync(this, incomingMessage); } else { protoViolationDisconnect = true; } } break; } } if (protoViolationDisconnect) { await messageProcessor.SendProtocolViolation(this); break; } if (disconnect) { break; } } } catch (Exception e) { if ((e is ObjectDisposedException) || (e is IOException)) { log.Info("Connection to client has been terminated."); } else { log.Error("Exception occurred: {0}", e.ToString()); } } log.Trace("(-)"); }