Click here to Skip to main content
15,867,989 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
Hi there, to enhance my current Software i wanted to switch the datalogic to a server application that has faster connection to the database and is able to cache data.

So far i wrote a working server app and a client that connects to this server.
Currently i am experiencing a performance issue on the server side resulting in high cpu load and i wanted to ask if anyone might find the problem of it.

The server and client shall communicate async, so that multiple clients can work with the server and the server will later build up a queue if the operations reach higher levels.

Below you'll find the code, it's a bit messy since it's just a prototype by now.
I already read several articles on CP and the interwebs on that and i think that the problem might occur through the loop back to the start accept.

Why? -> Because since the accept is done async i expect 4 threads to get stuck on the endAccept, though this is just an assumption, but it seems it's at least one.
Also, if i stop the server (close listening and stuff) i always get an exception on the server part where the endAccept is.

Would be happy for any advice or hint to the problem.
Thanks in afvance

What I have tried:

I wrote the server like this:

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Runtime.Serialization;
using System.Security.Cryptography.X509Certificates;

namespace ImsServer.ImsSockets
{
    public class ServerSocket
    {
        private TcpListener _listeningSocket;
        private readonly IPEndPoint _localEndPoint;
        private ServerMain _serverMainUi;
        List<TcpClient> _connectedClients = new List<TcpClient>(500);
        private byte[] _bufferForMessages = new byte[256];

        public ServerSocket(ServerMain InUi)
        {
            //Get Settings from Config File
            _localEndPoint = new IPEndPoint(IPAddress.Any, 12000);
            _serverMainUi = InUi;

            StartListening();
        }

        internal void StartListening()
        {
            try
            {
                _listeningSocket = new TcpListener(_localEndPoint);
                _listeningSocket.Start(100);

                StartAccept();
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }         
        }

        internal void StartAccept()
        {
            //Create an AsyncCallbackForAccept and go to ProcessAccept on completion
            AsyncCallback _asyncCallbackForAccept = ProcessAccept;

            //Begin the Accept
            _listeningSocket.BeginAcceptTcpClient(_asyncCallbackForAccept, _listeningSocket);
        }

        private async void ProcessAccept(IAsyncResult InAcceptAsyncResult)
        {
            //Get the Listener from the AsyncResult
            TcpListener _asyncListener = (TcpListener) InAcceptAsyncResult.AsyncState;

            //Accept the client connection
            TcpClient _clientToAccept = _asyncListener.EndAcceptTcpClient(InAcceptAsyncResult);

            await _serverMainUi.SetServerInformationText(Environment.NewLine + "Client : " + _clientToAccept.Client.LocalEndPoint + " Connected");

            _connectedClients.Add(_clientToAccept);

            //SslStream _clientStream = new SslStream(_clientToAccept.GetStream(), false);
            NetworkStream _clientStream = _clientToAccept.GetStream();

            //_clientStream.AuthenticateAsServer(new X509Certificate(), false, true);
            _clientStream.ReadTimeout = 5000;
            _clientStream.WriteTimeout = 5000;

            int _clientIndex = _connectedClients.FindIndex(Cc => Cc == _clientToAccept);

            byte[] _serverMessageToClient = Encoding.UTF8.GetBytes("You are connected to IMS-Server : Client No " + _clientIndex);
            _clientStream.Write(_serverMessageToClient, 0, _serverMessageToClient.Length);

            await _serverMainUi.SetServerInformationText(Environment.NewLine + "Connected Clients : " + _connectedClients.Count);

            _clientStream.Flush();

            //Loop back to accept other requests
            StartAccept();

            while(!_clientStream.DataAvailable) { }

            _clientStream.BeginRead(_bufferForMessages, 0, _bufferForMessages.Length, ReceiveClientMessageCallback, _clientToAccept);
        }

        private async void ReceiveClientMessageCallback(IAsyncResult InAsyncResult)
        {
            //TcpClient _client = _connectedClients.Find(Cc => Cc == (TcpClient)InAsyncResult.AsyncState);
            TcpClient _client = (TcpClient)InAsyncResult.AsyncState;

            if (_client.Connected)
            {
                NetworkStream _clientStream = _client.GetStream();
                byte[] _clientMessage = _bufferForMessages;
                int _noOfBytes = _clientStream.EndRead(InAsyncResult);
                string _clientDecodedMessage = "";

                _clientDecodedMessage = String.Concat(_clientDecodedMessage, Encoding.UTF8.GetString(_clientMessage, 0, _noOfBytes));

                await _serverMainUi.SetServerInformationText(Environment.NewLine + "Client : " + _connectedClients.FindIndex(Cc => Cc == _client) + " wants to " + _clientDecodedMessage);

                if (_clientDecodedMessage == "Disconnect")
                {
                    //We accept the disconnect wish and send a bye bye
                    byte[] _disconnectMessage = Encoding.UTF8.GetBytes("Bye");
                    _clientStream.Write(_disconnectMessage, 0, _disconnectMessage.Length);

                    _connectedClients.Remove(_client);
                }
            }
            else
            {
                _client.Close();
            }
        }

        private void AcceptRequestCompleted(object Sender, SocketAsyncEventArgs InArgs)
        {
            //ProcessAccept(InArgs);
        }

        public bool ServerSocketStop()
        {
            bool _connectionsPending = _listeningSocket.Pending();

            while(_connectionsPending) { }
            _listeningSocket.Stop();
            return true;
        }
    }
}


The client does this:

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace ServerMiniTester
{
    /// <summary>
    /// Interaktionslogik für MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private TcpClient _client;
        private byte[] _serverResponse = new byte[256];

        public MainWindow()
        {
            InitializeComponent();
        }

        private void BtConnectToServerClick(object sender, RoutedEventArgs e)
        {
            _client = new TcpClient();

            try
            {
                _client.Connect(IPAddress.Parse("10.176.2.8"),12000);

                while(!_client.Connected) { }

                //SslStream _clientStream = new SslStream(_client.GetStream(), false, ValidateServerCertificate, null);
                NetworkStream _clientStream = _client.GetStream();

                //while(!_clientStream.CanRead) { }

                //_clientStream.AuthenticateAsClient("10.176.2.8");
                AsyncCallback _receiveServerMessage = ReceiveServerMessageCallback;

                _serverResponse = new byte[256];
                _clientStream.BeginRead(_serverResponse, 0, _serverResponse.Length, _receiveServerMessage, _client);
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }
        }

        private void BtDisconnectServerClick(object Sender, RoutedEventArgs E)
        {           
            try
            {
                NetworkStream _clientStream = _client.GetStream();

                byte[] _disconnectMessage = Encoding.UTF8.GetBytes("Disconnect");
                _clientStream.Write(_disconnectMessage, 0, _disconnectMessage.Length);

                while(!_clientStream.DataAvailable) { Thread.Sleep(50);}

                AsyncCallback _receiveServerMessage = ReceiveServerMessageCallback;

                _serverResponse = new byte[256];
                _clientStream.BeginRead(_serverResponse, 0, _serverResponse.Length, _receiveServerMessage, _client);
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }
        }

        private async void ReceiveServerMessageCallback(IAsyncResult InAsyncResult)
        {
            NetworkStream _clientStream = ((TcpClient)InAsyncResult.AsyncState).GetStream();
            byte[] _serverMessage = _serverResponse;
            int _noOfBytes = _clientStream.EndRead(InAsyncResult);

            string _serverDecodedMessage = "";

            _serverDecodedMessage = String.Concat(_serverDecodedMessage, Encoding.UTF8.GetString(_serverMessage, 0, _noOfBytes));

            if (_serverDecodedMessage == "Bye")
            {
                await SetClientInformationText(Environment.NewLine + "Server response : " + _serverDecodedMessage +
                                               Environment.NewLine + "*****Server accepted Disconnect*****");

                _client.Close();
                _client.Dispose();

                await SetClientInformationText(Environment.NewLine + "*****Disconnected from Server*****");
            }
            else
            {
                await SetClientInformationText(Environment.NewLine + "Server response : " + _serverDecodedMessage);
            }
        }

        public async Task SetClientInformationText(string InText)
        {
            await LbServerResponse.Dispatcher.InvokeAsync(() => LbServerResponse.Text += InText);
        }

        private bool ValidateServerCertificate(Object Sender, X509Certificate InCertificate, X509Chain InChain, SslPolicyErrors InSslPolicyErrors)
        {
            return true;
        }
    }
}
Posted
Updated 18-Jan-19 6:05am
Comments
RickZeeland 18-Jan-19 4:17am    
I always struggle with Async code, to the point that I have given up on using it. I use an oldfashioned TcpReceiver static class which uses Threads for each Client message and it works fine !
HobbyProggy 18-Jan-19 8:15am    
As i did not get to this point yet, i'll try the async code. But if i need to fall back to oldfashioned (TM) working code i'd call you okay?
RickZeeland 18-Jan-19 8:19am    
Fine with me, it would be best if you got the Async code working, this should have advantages especially when you have lots of clients, in the old way this would mean a new Thread for each client !

1 solution

You've got an interesting mix of different types of async code in there! And I can see unguarded use of collection cross-thread etc.

But this:
while(!_clientStream.DataAvailable) { }

looks like a very tight loop, which is likely to get your CPU fan attempting take off.

At the very least slap a Thread.Sleep(0); in there, but obviously that is far from optimal!
 
Share this answer
 
Comments
HobbyProggy 21-Jan-19 4:02am    
Yeah, i am new to this stuff, the empty while is actually just for testing purposes. But if that would be the problem of my cpu load i'd change it to an amount that makes sense.
HobbyProggy 21-Jan-19 5:37am    
Oh wow, it totally did the trick! The CPU is chillin now, even with 25 clients connected.

Thanks alot!
Rob Philpott 21-Jan-19 8:29am    
Nice! :)
HobbyProggy 22-Jan-19 1:59am    
Btw, does sleep 100 make sense according to processing times?
Rob Philpott 22-Jan-19 10:39am    
Not sure I understand the question. Sleep will halt execution of the current thread and won't schedule any more until the amount of time has elapsed. Meanwhile, your thread which is now in a coma, is still using its resources - stack space and the like. This probably doesn't matter too much in a typical app, but when you move server-side to a service that might handle a lot of concurrent connections you need to be careful, you could end up with a lot of sleeping threads which do nothing but consume resources. It doesn't scale.

So, you want to avoid any kind of sleep server side. You could use Task.Delay instead (if in async method) which is much better but the general premise of waiting server side is a bit alarming. Instead aim to react when things happen - data being received etc. via use of async methods.



This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900