/// <summary>
        /// Sends a message to the client over the open network stream.
        /// </summary>
        /// <param name="Message">Message to send.</param>
        /// <returns>true if the message was sent successfully to the target recipient.</returns>
        protected async Task <bool> SendMessageInternalAsync(Message Message)
        {
            log.Trace("()");

            bool res = false;

            string msgStr = Message.ToString();

            log.Trace("Sending message:\n{0}", msgStr.SubstrMax(512));
            byte[] messageBytes = ProtocolHelper.GetMessageBytes(Message);

            await streamWriteLock.WaitAsync();

            try
            {
                if (Stream != null)
                {
                    await Stream.WriteAsync(messageBytes, 0, messageBytes.Length);

                    res = true;
                }
                else
                {
                    log.Info("Connection to the client has been terminated.");
                }
            }
            catch (IOException)
            {
                log.Info("Connection to the client has been terminated.");
            }
            finally
            {
                streamWriteLock.Release();
            }

            log.Trace("(-):{0}", res);
            return(res);
        }
Beispiel #2
0
        /// <summary>
        /// Asynchronous read and processing function for each client that connects to the TCP server.
        /// </summary>
        /// <param name="Client">Client that is connected to TCP server.</param>
        /// <remarks>The client is being handled in the processing loop until the connection to it
        /// is terminated by either side. This function implements reading the message from the network stream,
        /// which includes reading the message length prefix followed by the entire message.</remarks>
        private async void ClientHandlerAsync(Client Client)
        {
            this.log.Info("(Client.RemoteEndPoint:{0})", Client.RemoteEndPoint);

            string       prefix = string.Format("{0}[{1}] ", this.logPrefix, Client.RemoteEndPoint);
            PrefixLogger log    = new PrefixLogger(this.logName, prefix);

            clientList.AddNetworkPeer(Client);
            log.Debug("Client ID set to 0x{0:X16}.", Client.Id);

            await Client.ReceiveMessageLoop();

            // Free resources used by the client.
            clientList.RemoveNetworkPeer(Client);
            await Client.HandleDisconnect();

            Client.Dispose();

            log.Info("(-)");
        }
Beispiel #3
0
        /// <summary>
        /// <para>Starts the TCP server listener and starts client thread handlers.</para>
        /// <para>If the application is restarted, it may be the case that the TCP port
        /// is unusable for a short period of time. This method repeatedly tries to reuse that port until it succeeds
        /// or until 10 unsuccessful attempts are reached.</para>
        /// </summary>
        /// <returns>true if the function succeeds, false otherwise</returns>
        public bool Start()
        {
            log.Info("(Roles:[{0}])", this.Roles);

            int  tryCounter = 0;
            bool res        = false;

            while (tryCounter < 10)
            {
                try
                {
                    this.Listener.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                    this.Listener.Start();
                    res = true;
                    break;
                }
                catch (SocketException se)
                {
                    log.Info("Socket error code {0} occurred while trying to reuse socket: {1}.", se.SocketErrorCode, se.ToString());
                }

                int waitTime = tryCounter * 3;
                log.Info("Will wait {0} seconds and then try again.", waitTime);
                Thread.Sleep(waitTime * 1000);
                tryCounter--;
            }

            if (res)
            {
                clientQueueHandlerThread = new Thread(new ThreadStart(ClientQueueHandlerThread));
                clientQueueHandlerThread.Start();

                acceptThread = new Thread(new ThreadStart(AcceptThread));
                acceptThread.Start();

                IsRunning = true;
            }

            log.Info("(-):{0}", res);
            return(res);
        }
Beispiel #4
0
        /// <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("(-)");
        }