Click here to Skip to main content
15,881,248 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
hi i'm david and i'm new in this site, i'm here because i have a problem with my solution of Client-Server communication.
I have created 3 classes 'STransferMessage', 'SServer' and 'SClient'.
SServer and SClient extend STransferMessage, i thought it might be the best solution because more methods are similar.
Now i describe you my problem;
All works very good on Windows Console, but when i use the same classes on Windows Forms or WPF and one client try to connect, the server crash with "The operation completed successfully" exception! no one try-catch costruct block the exception and the source aren't available.
So i decided to create a new empty form but i have had the same problem.
I can't understand why with Console works and with forms or WPF no.

these are my classes:
Dropbox folder


i'm sorry for my bad english but i'm italian.

Here the codes

STransferMessage
C#
using System;
using System.Net.Security;
using System.Net.Sockets;
using System.Text;

namespace SecureTransferMessage.Communication
{
    public enum TransferSystemType
    {
        Server,
        Client
    }

    public class STransferMessage
    {
        private readonly Boolean _isServer;
        private readonly TransferSystemType _type;

        public STransferMessage()
        {
            _type = TransferSystemType.Client;
            _isServer = false;
        }

        public STransferMessage(TransferSystemType type)
        {
            _type = type;
            _isServer = (_type == TransferSystemType.Server) ? true : false;
        }

        protected internal TransferSystemType Type
        {
            get { return _type; }
        }

        protected internal Boolean IsServer
        {
            get { return _isServer; }
        }

        protected internal Boolean IsClient
        {
            get { return !_isServer; }
        }


        protected internal Boolean Write(TcpClient client, String message)
        {
            NetworkStream clientStream;

            if (client == null)
            {
                return false;
            }
            if (client.Connected)
                clientStream = client.GetStream();
            else
                return false;

            return Write(clientStream, message);
        }

        protected internal String Read(TcpClient client)
        {
            NetworkStream clientStream;

            if (client == null)
            {
                return null;
            }
            if (client.Connected)
                clientStream = client.GetStream();
            else
                return null;

            return Read(clientStream);
        }


        protected internal Boolean Write(NetworkStream clientStream, String message)
        {
            if (clientStream != null && clientStream.CanWrite)
            {
                var sStream = new SslStream(clientStream);

                Byte[] mess = Encoding.UTF8.GetBytes(message);
                try
                {
                    clientStream.Write(mess, 0, mess.Length);
                    return true;
                }
                catch (Exception)
                {
                    throw;
                }
            }
            return false;
        }

        protected internal String Read(NetworkStream clientStream)
        {
            if (clientStream != null && clientStream.DataAvailable && clientStream.CanRead)
            {
                var readBuffer = new byte[1024];
                var message = new StringBuilder();
                var sStream = new SslStream(clientStream);


                do
                {
                    int bytesRead = clientStream.Read(readBuffer, 0, readBuffer.Length);
                    //int bytesRead = sStream.Read(readBuffer, 0, readBuffer.Length);
                    message.AppendFormat("{0}", Encoding.UTF8.GetString(readBuffer, 0, bytesRead));
                } while (clientStream.DataAvailable);
                return message.ToString();
            }
            return null;
        }

        protected internal Boolean SocketIdConnected(Socket nSocket)
        {
            if (nSocket.Connected)
            {
                if ((nSocket.Poll(0, SelectMode.SelectWrite)) && (!nSocket.Poll(0, SelectMode.SelectError)))
                {
                    var buffer = new byte[1];
                    if (nSocket.Receive(buffer, SocketFlags.Peek) == 0)
                    {
                        return false;
                    }
                    return true;
                }
                return false;
            }
            return false;
        }
    }
}



SServer
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace SecureTransferMessage.Communication
{
    public class SServer : STransferMessage
    {
        #region "VARIABILI"

        //CAMPI PRIVATI
        public const int NO_CLIENT_LIMIT = -1;
        private Thread _checkClientThread;
        private Dictionary<String, TcpClient> _clients;
        private Boolean _isMulticast;
        private TcpListener _listener;
        private int _maxClient = NO_CLIENT_LIMIT;
        //CAMPI PUBBLICI
        public Dictionary<String, TcpClient> Clients
        {
            get { return _clients; }
        }

        public Boolean IsMulticast
        {
            get { return _isMulticast; }
        }

        public Boolean HasClient
        {
            get { return (_clients.Count > 0) ? true : false; }
        }

        public int MaxClient
        {
            get { return _maxClient; }
            set { _maxClient = value; }
        }

        #endregion

        #region "EVENTI"
        //EVENT HANDLER
        public class CommunicationMessageEventArgs : EventArgs
        {
            public TcpClient Client;
            public String ClientId;
            public String Message;
            public DateTime Time;
        }

        //EVENT
        //start & stop
        public event EventHandler<EventArgs> ServerStarting;
        public event EventHandler<EventArgs> ServerStopping;

        //send & received
        public event EventHandler<CommunicationMessageEventArgs> ServerReceivedMessage;
        public event EventHandler<CommunicationMessageEventArgs> ServerSentMessage;

        //other
        public event EventHandler<EventArgs> ClientDisconnected;

        //METHOD

        //start & stop
        private void OnServerStarting(EventArgs e)
        {
            if (ServerReceivedMessage != null)
            {
                ServerStarting(this, e);
            }
        }
        private void OnServerStopping(EventArgs e)
        {
            if (ServerSentMessage != null)
            {
                ServerStopping(this, e);
            }
        }

        //send & received
        private void OnReceivedMessage(CommunicationMessageEventArgs e)
        {
            if (ServerReceivedMessage != null)
            {
                ServerReceivedMessage(this, e);
            }
        }
        private void OnSentMessage(CommunicationMessageEventArgs e)
        {
            if (ServerSentMessage != null)
            {
                ServerSentMessage(this, e);
            }
        }

        //other
        private void OnClientDisconnected(EventArgs e)
        {
            if (ClientDisconnected != null)
            {
                ClientDisconnected(this, e);
            }
        }
        #endregion

        #region "COSTRUTTORI"

        /// <summary>
        ///     Inizializza il server
        /// </summary>
        /// <param name="port">
        ///     Determina su quale porta il server sarà aperto, con 0 viene assegnata una porta libera dal sistema
        ///     operativo
        /// </param>
        public SServer(int port)
            : base(TransferSystemType.Server)
        {
            _iniServer(port, false);
        }

        /// <summary>
        ///     Inizializza il server
        /// </summary>
        /// <param name="port">
        ///     Determina su quale porta il server sarà aperto, con 0 viene assegnata una porta libera dal sistema
        ///     operativo
        /// </param>
        /// <param name="isMulticast">Determina se il server sarà multicast o no</param>
        public SServer(int port, Boolean isMulticast)
            : base(TransferSystemType.Server)
        {
            _iniServer(port, isMulticast);
        }

        #endregion

        #region "PUBBLICI"

        /// <summary>
        ///     Avvia il server in attesa di richieste da parte dei client
        /// </summary>
        public void Start()
        {
            //invoco l'evento ServerStarting quando il server è in fase di starting
            OnServerStarting(new EventArgs());

            _listener.Start();
            Console.WriteLine("Waiting for client..");
            _listener.BeginAcceptTcpClient(AccettaClient, _listener);
            _checkClientThread.Start();
        }

        /// <summary>
        ///     Chiude il server e la sessione con tutti i client
        /// </summary>
        public void Stop()
        {
            //invoco l'evento ServerStopping quando il server è in fase di stopping
            OnServerStopping(new EventArgs());

            _listener.Stop();
            _checkClientThread.Abort();
        }


        private void AccettaClient(IAsyncResult result)
        {
            var listener = (TcpListener) result.AsyncState;
            TcpClient client;
            try
            {
                DeleteDeadClients();
                client = listener.EndAcceptTcpClient(result);

                if (HasClient && !_isMulticast)
                {
                    Console.WriteLine("Server full!");
                    Write(client, "Server full!");
                }
                else if (_maxClient != NO_CLIENT_LIMIT && _clients.Count >= _maxClient)
                {
                    Console.WriteLine("Server full!");
                    Write(client, "Server full!");
                }
                else
                {
                    String id = Guid.NewGuid().ToString();

                    //Client connesso!
                    Clients.Add(id, client);
                    Console.WriteLine("Client connesso!");
                    Console.Title = "Server [" + Clients.Count + " Client]";
                }
            }
            catch (SocketException ex)
            {
                Console.WriteLine("Error accepting TCP connection: {0}", ex.Message);
                return;
            }
            catch (ObjectDisposedException)
            {
                Console.WriteLine("Listen canceled.");
                return;
            }

            listener.BeginAcceptTcpClient(AccettaClient, listener);
        }


        /// <summary>
        ///     Scrive sullo stream del client un messaggio
        /// </summary>
        /// <param name="message">Il messaggio da spedire al client</param>
        /// <returns>Ritorna TRUE se l'operazione di scrittura è andata a buon fine, FALSE in caso contrario.</returns>
        public Boolean Write(String message)
        {
            //Verifico se la pila dei client non è vuota e poi richiamo il metodo Read per la lettura del stream del client
            if (HasClient)
            {
                var Kclient = _clients.First();
                String clientId = Kclient.Key;
                if (Write(clientId, message))
                {
                    var args = new CommunicationMessageEventArgs
                    {
                        Message = message,
                        Client = Kclient.Value,
                        ClientId = clientId,
                        Time = DateTime.Now
                    };

                    //invoco l'evento SentMessage quando il messaggio è stato spedito
                    OnSentMessage(args);
                    return true;
                }
                else
                {
                    return false;
                }
            }
            return false;
        }

        /// <summary>
        ///     Avvia una lettura sullo stream del client.
        ///     Consigliato in un server unicast
        /// </summary>
        /// <returns>Ritorna una Stringa contenente i Byte letti dallo stream del client</returns>
        public String Read()
        {
            //Verifico se la pila dei client non è vuota e poi richiamo il metodo Read per la lettura del stream del client
            if (HasClient)
            {
                var kclient = _clients.First();
                String clientId = kclient.Key;

                String message = Read(clientId);
                if (message != null)
                { 
                    var args = new CommunicationMessageEventArgs
                    {
                        Message = message,
                        Client = kclient.Value,
                        ClientId = clientId,
                        Time = DateTime.Now
                    };

                    //invoco l'evento ReceivedMessage quando il messaggio è stato spedito
                    OnReceivedMessage(args);
                    return message;
                }
                
                return null;
            }
            return null;
        }


        /// <summary>
        ///     Scrive sullo stream del client specificato un messaggio
        /// </summary>
        /// <param name="clientId">Id del client sul cui stream verrà scritto il messaggio</param>
        /// <param name="message">Il messaggio da spedire al client</param>
        /// <returns>Ritorna TRUE se l'operazione di scrittura è andata a buon fine, FALSE in caso contrario.</returns>
        public Boolean Write(String clientId, String message)
        {
            return Write(GetClientById(clientId), message);
        }

        /// <summary>
        ///     Avvia una lettura sullo stream del client indicato.
        ///     Consigliato in un server multicast
        /// </summary>
        /// <param name="clientId">Id del client di cui si vuole leggero lo stream</param>
        /// <returns>Ritorna una Stringa contenente i Byte letti dallo stream del client</returns>
        public String Read(String clientId)
        {
            return Read(GetClientById(clientId));
        }


        public KeyValuePair<string, string> ReadFromAnyone()
        {
            var ret = new KeyValuePair<string, string>();

            var t = new Thread(() =>
            {
                while (true)
                {
                    if (ret.Key != null)
                    {
                        break;
                    }
                }
            });

            t.Start();

            foreach (var dclient in _clients)
            {
                KeyValuePair<String, TcpClient> dclient1 = dclient;
                new Thread(new ThreadStart(delegate
                {
                    Thread.Sleep(50);
                    String strR = this.Read(dclient1.Key);
                    ret = new KeyValuePair<string, string>(dclient1.Key, strR);
                })).Start();
            }

            t.Join();
            return ret;
        }

        public TcpClient GetClientById(String clientId)
        {
            TcpClient client = null;
            try
            {
                client = _clients[clientId];
            }
            catch (Exception)
            {
                client = null;
            }

            return client;
        }

        /// <summary>
        ///     Disconnette ed espelle tutti i client in lista
        /// </summary>
        public void KickAllClient()
        {
            Dictionary<string, TcpClient> tempClients = _clients;
            foreach (string clientId in tempClients.Keys.ToList())
            {
                KickClient(clientId);
            }
        }

        /// <summary>
        ///     Disconnette ed espelle il client con id specificato
        /// </summary>
        /// <param name="clientId">identificatore univoco del client</param>
        public Boolean KickClient(String clientId)
        {
            TcpClient client = GetClientById(clientId);

            if (client != null)
            {
                client.GetStream().Close();
                client.Close();
                bool res = _clients.Remove(clientId);

                //invoco l'evento ClientDisconnected
                OnClientDisconnected(new EventArgs());
                return res;
            }
            return false;
        }


        /// <summary>
        /// </summary>
        /// <param name="message">Messaggio da spedire in broadcast</param>
        /// <returns>TRUE se l'invio è andato a buon fine, FALSE in caso contrario.</returns>
        public int BroadcastMessage(String message)
        {
            return _clients.Count(dclient => Write(dclient.Value, message));
        }

        public void Dispose()
        {
            KickAllClient();
            _listener.Stop();
        }

        /// <summary>
        ///     Restisce l'idirizzo ipv4 del' host locale
        /// </summary>
        /// <returns></returns>
        public static IPAddress GetLocalIpAddress()
        {
            IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName());
            foreach (IPAddress ip in host.AddressList)
            {
                if (ip.AddressFamily == AddressFamily.InterNetwork)
                {
                    return ip;
                }
            }

            return null;
        }

        public void DeleteDeadClients()
        {
            Dictionary<string, TcpClient> tempClients = _clients;
            foreach (string clientId in tempClients.Keys.ToList())
            {
                if (!SocketIdConnected(tempClients[clientId].Client))
                {
                    KickClient(clientId);
                    Console.WriteLine("Cliend disconnesso");
                }
            }
        }

        #endregion

        #region "PRIVATE"

        private void _iniServer(int port, Boolean isMulticast)
        {
            _clients = new Dictionary<String, TcpClient>();
            _listener = new TcpListener(GetLocalIpAddress(), port);
            _isMulticast = isMulticast;


            //Avvio un thread parallelo per la verifica e la rimozione dei client disconnessi
            _checkClientThread = new Thread(new ThreadStart(delegate
            {
                while (true)
                {
                    if (HasClient)
                    {
                        DeleteDeadClients();
                        Console.Title = "Server [" + Clients.Count + " Client]";
                    }
                    else
                    {
                        Thread.Sleep(1000);
                    }
                }
            }));
        }

        #endregion
    }
}



SClient
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Channels;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.ComponentModel;
using System.Net.Security;
using SecureTransferMessage.Communication.STMException;

namespace SecureTransferMessage.Communication
{
    public sealed class SClient : STransferMessage
    {
        private TcpClient _client = null;
        private IPAddress _address = null;
        private int _port = 0;

        public IPAddress Address { get { return _address; } }

        public int Port { get { return _port; } }

        public Boolean IsConnected { get {
            try
            {
                return _client.Connected;
            }
            catch (Exception)
            {
                return false;
            } 
        } }


        public SClient(IPAddress address,int port) : base(TransferSystemType.Client)
        {
            this._address = address;
            this._port = port;
            Connect();
        }

        public void Connect()
        {
            if (_client == null)
            {
                //System.Windows.Forms.MessageBox.Show(_address.ToString()+": "+_port);     
                _client = new TcpClient(_address.ToString(), _port);                
            }
            else
            {
                throw new SecureTransferException("La connessione con l'host è già aperta");
            }
        }

        public Boolean TryConnect()
        {
            try
            {
                _client = new TcpClient(_address.ToString(), _port);
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        public void Close()
        {
            if (_client != null && IsConnected)
            {
                _client.GetStream().Close();
                _client.Close();
                _client = null;
            }
        }

        /// <summary>
        /// Scrive sullo stream verso il server un messaggio
        /// </summary>
        /// <param name="message">Il messaggio da spedire al server</param>
        /// <returns>Ritorna TRUE se l'operazione di scrittura è andata a buon fine, FALSE in caso contrario.</returns>
        public Boolean Write(String message)
        {
            return base.Write(_client, message);
        }


        /// <summary>
        /// Avvia una lettura sullo stream del client. 
        /// Consigliato in un server unicast
        /// </summary>
        /// <returns>Ritorna una Stringa contenente i Byte letti dallo stream del client</returns>
        public String Read()
        {
            return base.Read(_client);
        }

    }
}
Posted
Updated 16-Feb-15 22:55pm
v4
Comments
Rob Philpott 16-Feb-15 11:26am    
Could you add some code to your questions please? People are unlikely to use the dropbox link.
DavidGeirola 16-Feb-15 14:04pm    
yes sure i'm sorry but i thought it was too longer to post, done
Rob Philpott 16-Feb-15 14:19pm    
Thanks for posting it. First possibility - Console apps are safe for printing output to the screen from multiple threads. Gui apps aren't - updates have to be performed on the GUI thread itself. Have you ruled this out as a possible problem?
DavidGeirola 16-Feb-15 14:51pm    
thanks, I've just tried, i've created a new form, instantiated the SServer class and called only the Start() method so without any out but it crash at the same time.
Now that the exception is IOException could be the multithread the problem, but how can i fix that ? I do not interact with the GUI.
Rob Philpott 16-Feb-15 15:19pm    
What is the detail of the IOException? Is there any more info than that?

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