Search code examples
c#tcpchattcpclienttcplistener

TCP Chat : Server sends to multiple client


So i'm working on a C# Chat using TCP protocol and i cant figure out how to make the server send data received by a client to all the clients connected to him . So i tried to put all client into an arraylist and with the use of a "foreach" sending them data received by the server like in this topic but i failed . For sending and receiving data i'm using Streams (StreamWriter / StreamReader).Each client is handled in a different thread by the server.

Question : How to send data to all the clients at the same time ?

Server :

static void LoopClients()
{
    while (running)
    {
        TcpClient newClient = server.AcceptTcpClient();
        arrClient.add(newClient)

        Console.WriteLine("Connection accepted from " + ((IPEndPoint)newClient.Client.RemoteEndPoint).Address);

        Thread t = new Thread(new ParameterizedThreadStart(HandleClient));
        t.Start(newClient);
    }
}

static void HandleClient(object obj)
{
    TcpClient client = (TcpClient)obj;

    StreamWriter Writer = new StreamWriter(client.GetStream(), Encoding.ASCII);
    StreamReader Reader = new StreamReader(client.GetStream(), Encoding.ASCII);

    Boolean ClientConnected = true;
    String Data = null;
    var LEP = client.Client.RemoteEndPoint as IPEndPoint;
    var LAD = LEP.Address;


    while (ClientConnected)
    {
        Data = Reader.ReadLine();

        Console.WriteLine(""+ LAD + " : " + Data);

        Writer.WriteLine(LAD+" : "+Data+"");
        Writer.Flush();
    }
}

Thank You !


Solution

  • Essentially, you need to track all the clients somehow. This could be as simple as tracking all the StreamWriter in a synchronized collection, and ensure you remove from it when sessions terminate. For example:

    StreamWriter Writer = new StreamWriter(client.GetStream(), Encoding.ASCII);
    try {
        lock(allClients) { allClients.Add(Writer); }
        while (ClientConnected)
        {
            ...
        }
    } finally {
        lock(allClients) { allClients.Remove(Writer); }
    }
    

    Now we need to do something when we want to send a message to everyone. Perhaps the simplest thing to to is a synchronized sweep:

    lock(allClients) {
        foreach(var writer in allClients)
            try { writer.Send(message); } catch { /* log */ }
    }
    

    This synchronizes the entire collection - so as long as this is the only place that sends messages, then you know a: that you're never trying to send to the same socket twice at once, and b: that you're not going to break the iterator by having a socket add/remove.

    Caveat: this is a very very crude and basic implementation of a multi-client server, and should really only be used as an introduction to the topic. "Real" multi-client servers should be much more paranoid that this.