0

I wrote a client-server app, this is a console chat application for many clients. When only one client is connected, the application works well, but when two or more clients are connected I have a bug, after sending one message to the second client, he lost connection to the server and only first client can send a message to the server...

I used Task for asynchronous operations like listening port and sending messages. When one client sends a message to the server, it adds it to the list messages and resends to all clients to refresh all windows.

Server application:

using System;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Server
{
    class Program
    {
        private static List<Client> clients = new List<Client>();
        private static TcpListener  listener = null;
        private static StreamReader reader = null;
        private static StreamWriter writer = null;
        private static List<Task>   clientTasks = new List<Task>();
        private static List<string> messages = new List<string>();

        public static void Main()
        {
            Console.Title = "Server";
            try
            {
                listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 8080);
                listener.Start();
                Console.WriteLine("Server started...");

                var connectTask = Task.Run(() => ConnectClients());
                //var listenTask = Task.Run(() => ListenClients());

                Task.WaitAll(connectTask);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
            finally
            {
                if (listener != null)
                {
                    listener.Stop();
                }
            }
        }

        private static void ConnectClients()
        {
            Console.WriteLine("Waiting for incoming client connections...");
            while (true)
            {
                if (listener.Pending()) //if someone want to connect
                {
                    clients.Add(new Client(listener.AcceptTcpClient(), "Client: " + (clients.Count + 1)));
                    Console.WriteLine(clients[clients.Count - 1].clientName + " connected to server.");
                    var newClientTask = Task.Run(() => HandleClient(clients[clients.Count - 1]));
                    clientTasks.Add(newClientTask); //start new task for new client
                }
            }
        }

        private static void HandleClient(Client TCPClient)
        {
            Console.WriteLine("Starting handle client");
            string s = string.Empty;
            writer = new StreamWriter(TCPClient.client.GetStream());
            reader = new StreamReader(TCPClient.client.GetStream());

            try
            {
                while (!(s = reader.ReadLine()).Equals("Exit") || (s == null))
                {
                    if(!TCPClient.client.Connected)
                    {
                        Console.WriteLine("Client disconnected.");
                        clients.Remove(TCPClient);
                    }

                    Console.WriteLine("From client: " + TCPClient.clientName  + " -> " + s);
                    messages.Add(TCPClient.clientName + ": " + s); //save new message
                    //Console.WriteLine(s);

                    foreach (Client c in clients) //refresh all connected clients
                    {
                        c.writer.WriteLine("%C"); //clear client
                        foreach (string msg in messages)
                        {
                            c.writer.WriteLine(msg);
                            c.writer.Flush();
                        }
                    }
                }
                //CloseServer();
            }
            catch (Exception e) { Console.WriteLine(e); }
            Console.WriteLine("ending handle client");
        }

        private static void CloseServer()
        {
            reader.Close();
            writer.Close();
            clients.ForEach(tcpClient => tcpClient.client.Close());
        }
    }
}

Client information class:

using System.Net.Sockets;
using System.IO;

namespace Server
{
    class Client
    {
        public TcpClient client;
        public StreamWriter writer;
        public string clientName;

        public Client(TcpClient client, string clientName)
        {
            this.client = client;
            writer = new StreamWriter(client.GetStream());
            this.clientName = clientName;
        }
    }
}

Client application:

using System.Net.Sockets;
using System.Net;
using System.IO;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace Client
{
    class Program
    {
        private static TcpClient    client = new TcpClient();
        private static StreamReader reader;
        private static StreamWriter writer;

        private static bool         refresh;
        private static List<string> messages = new List<string>();

        public static void Main()
        {
            Console.Title = "Client";

            ConnectLoop();


            //Task.WaitAll(sendTask, recieveTask); //wait for end of all tasks
        }

        private static void ConnectLoop()
        {
            bool refreshTask = false;

            Task sendTask = null, recieveTask = null, updateConvTask = null;

            while (true)
            {
                if(!client.Connected) //try to connect
                {
                    refreshTask = true;
                    if(sendTask != null || recieveTask != null || updateConvTask != null)
                    {
                        sendTask.Dispose();
                        recieveTask.Dispose();
                        updateConvTask.Dispose();

                        sendTask = recieveTask = updateConvTask = null;
                    }

                    Console.WriteLine("Connecting to server...");

                    try
                    {
                        client.Connect(IPAddress.Parse("127.0.0.1"), 8080);
                    }
                    catch (SocketException) { }

                    Thread.Sleep(10);
                }
                else if(refreshTask) // \/ CONNECTED \/
                {
                    Console.WriteLine("Connected.");

                    reader = new StreamReader(client.GetStream());
                    writer = new StreamWriter(client.GetStream());

                    sendTask       = Task.Run(() => SendMessage());          //task for sending messages
                    recieveTask    = Task.Run(() => RecieveMessage());       //task for recieving messages
                    updateConvTask = Task.Run(() => UpdateConversation());   //task for update console window

                    refreshTask = false;
                }
            }
        }

        private static void SendMessage()
        {
            string msgToSend = string.Empty;
            do
            {
                Console.WriteLine("Enter a message to send to the server");
                msgToSend = Console.ReadLine();
                writer.WriteLine(msgToSend);
                writer.Flush();
            } while (!msgToSend.Equals("Exit"));
            EndConnection();
        }

        private static void RecieveMessage()
        {
            try
            {
                while (client.Connected)
                {
                    //Console.Clear();
                    string msg = reader.ReadLine();

                    if(msg != string.Empty)
                    {
                        if (msg == "%C") //special message from server, clear messages if recieve it
                        {
                            messages.Clear();
                        }
                        else
                        {
                            messages.Add(msg);
                            refresh = true; //refresh console window
                        }
                    }
                    //Console.Clear();
                    //Console.WriteLine(msgFromServer);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
        }

        private static void UpdateConversation()
        {
            //string conversationTmp = string.Empty;

            try
            {
                while (true)
                {
                    if (refresh) //only if refresh
                    {
                        refresh = false;
                        Console.Clear();
                        messages.ForEach(msg => Console.WriteLine(msg)); //write all messages
                        Console.WriteLine();
                    }
                }
            }
            catch (Exception) { }
        }

        private static void EndConnection()
        {
            reader.Close();
            writer.Close();
            client.Close();
        }

    }
}

I know that my bug will be something stupid. I'm new to TCP/IP applications, could you give me links to some tutorials that use it and Tasks?

Thanks for any help.

manuzi1
  • 1,068
  • 8
  • 18
Michael
  • 449
  • 1
  • 6
  • 13
  • Have a look to this answer from @spender http://stackoverflow.com/a/5742874/920557 – Eugene Komisarenko Apr 18 '17 at 13:08
  • @spender uses IAsyncResult, I want to use Tasks – Michael Apr 18 '17 at 13:25
  • There's been many chat applications written, why reinvent the wheel? Unless you have very good reason, and know how, to work directly with a `TcpClient` you probably shouldn't. Here's a good place to start with a custom chat app [SignalR Chat](https://learn.microsoft.com/en-us/aspnet/signalr/overview/getting-started/tutorial-getting-started-with-signalr) – JSteward Apr 18 '17 at 15:42
  • There is no need to dispose tasks. And you're ending the connection in client code, so what's your bug exactly? – VMAtm Apr 18 '17 at 16:07
  • @JSteward I don't want to use ASP.NET. – Michael Apr 18 '17 at 17:14
  • @VMAtm This is a console application, so you can simply paste this code and check it, I will try to describe the problem. first client connects and normally send message, second client too, after that when first client try to send another message it can't, second client can, and second client sends two messages, server has connection with both them and refreshs them, second client sends messages for both clients, one time like client 1 next time like client 2. – Michael Apr 18 '17 at 17:28

0 Answers0