Search code examples
c#tcpclientbinaryreaderbinarywriterserver-communication

Using BinaryReader/BinaryWriter to build a chat


Hello I'm trying to build a chat using BinaryReader/BinaryWriter, I came into a dead end where I can't figure out how do I make my server send the message to all connected clients..

I have tried adding all clients to a list and running foreach loop on the list to send the message to every connected client, that didn't workout..

Server:

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

namespace server {
    internal class Program {
        public static int counter = 0;
        //List<Client> clientList = new List <Client>();

        private static readonly IPAddress sr_ipAddress = IPAddress.Parse("127.0.0.1");
        public TcpListener Listener = new TcpListener(sr_ipAddress, 8888);
        public static void Main(string[] args) {
            Program server = new Program();
            server.Start();
            Console.ReadKey();
        }
        public void Start() {
            Listener.Start();
            Console.WriteLine("Server started");
            StartAccept();
        }
        private void StartAccept() {
            Listener.BeginAcceptTcpClient(HandleAsyncConnection, Listener);
        }
        public void HandleAsyncConnection(IAsyncResult res) {
            StartAccept(); //listen for new connections again
            TcpClient clientSocket = Listener.EndAcceptTcpClient(res);
            Client client = new Client(clientSocket);
            client.StartClient();
        }
    }

    internal class Client {
        public TcpClient ClientSocket;
        public string CleintName{get; set;}

        public Client(TcpClient inClientSocket) {
            ClientSocket = inClientSocket;
            NetworkStream netStream = ClientSocket.GetStream();
            BinaryReader Listen = new BinaryReader(netStream);
            CleintName = Listen.ReadString();
        }
        public void StartClient() {
            Thread clientThread = new Thread(Chat);
            clientThread.Start();
            Console.WriteLine("New Client connected {0} => {1}", ClientSocket.GetHashCode(), CleintName);
        }

        private string _message;
        //private string _serverMessage;
        public void Chat() {
            NetworkStream netStream = ClientSocket.GetStream();
            BinaryReader listen = new BinaryReader(netStream);
            BinaryWriter send = new BinaryWriter(netStream);
            while (true) {
                //listening int1
                try {
                    _message = listen.ReadString();
                    Console.WriteLine("{0} :{1}", CleintName, _message);
                    send.Write(CleintName + ": " + _message);
                    //Send.Write(CleintName + " :" + _message);
                }
                catch (Exception ex) {
                    listen.Close();
                    send.Close();
                    netStream.Close();
                    Console.WriteLine(ex.Message);
                    return;
                }
            }
        }
    }
}

Client:

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

namespace tcpClient {
    internal class Program {
        private static readonly IPAddress s_ipAddress = IPAddress.Parse("127.0.0.1");
        private static readonly TcpClient s_client = new TcpClient();

        private static void Main(string[] args) {
            //Console.WriteLine("Press Enter to start");
            //Console.ReadLine();
            try {
                s_client.Connect(s_ipAddress, 8888);
            }
            catch (Exception ex) {
                Console.WriteLine("{0}", ex.Message);
                Console.ReadKey();
                return;
            }
            NetworkStream netStream = s_client.GetStream();
            BinaryReader listen = new BinaryReader(netStream);
            BinaryWriter send = new BinaryWriter(netStream);
            Console.WriteLine("Connected");
            Console.Write("Enter name: ");
            string name = Console.ReadLine();
            send.Write(name);
            Console.WriteLine("Chat started");
            while (true) {
                var message = Console.ReadLine();
                send.Write(message);
                //Console.WriteLine("{0}: {1}", name, message);
                Console.WriteLine(listen.ReadString());
            }
        }
    }
}

Solution

  • Your code is problematic in both client and server. @Mike Nakis answer already covered the former, now I'm going to cover the later.

    I have tried adding all clients to a list and running foreach loop on the list to send the message to every connected client, that didn't workout..

    I don't know what didn't workout, but there is just no other way - you have to keep some sort of list with the connected clients. The clients (here clients means client connections) have to keep reference to the server and notify it for a received message. The server will broadcast the message to the all currently connected clients.

    Here is a quick and dirty version of your server code which does just that:

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Net;
    using System.Net.Sockets;
    using System.Threading;
    
    namespace server
    {
        internal class Server
        {
            private static readonly IPAddress sr_ipAddress = IPAddress.Parse("127.0.0.1");
            public static void Main(string[] args)
            {
                Server server = new Server();
                server.Start();
                Console.ReadKey();
            }
            TcpListener Listener = new TcpListener(sr_ipAddress, 8888);
            HashSet<Client> Clients = new HashSet<Client>();
            object syncGate = new object();
            public void Start()
            {
                Listener.Start();
                Console.WriteLine("Server started");
                StartAccept();
            }
            private void StartAccept()
            {
                Listener.BeginAcceptTcpClient(HandleAsyncConnection, Listener);
            }
            private void HandleAsyncConnection(IAsyncResult res)
            {
                StartAccept(); //listen for new connections again
                var clientSocket = Listener.EndAcceptTcpClient(res);
                var client = new Client(this, clientSocket);
                client.StartClient();
                lock (syncGate)
                {
                    Clients.Add(client);
                    Console.WriteLine("New Client connected {0} => {1}", client.ClientSocket.GetHashCode(), client.ClientName);
                }
            }
            internal void OnDisconnected(Client client)
            {
                lock (syncGate)
                {
                    Clients.Remove(client);
                    Console.WriteLine("Client disconnected {0} => {1}", client.ClientSocket.GetHashCode(), client.ClientName);
                }
            }
            internal void OnMessageReceived(Client sender, string message)
            {
                lock (syncGate)
                {
                    Console.WriteLine("{0}: {1}", sender.ClientName, message);
                    foreach (var client in Clients)
                        client.OnMessageReceived(sender, message);
                }
            }
        }
    
        internal class Client
        {
            public readonly Server Server;
            public TcpClient ClientSocket;
            public string ClientName { get; set; }
            public Client(Server server, TcpClient clientSocket)
            {
                Server = server;
                ClientSocket = clientSocket;
                var netStream = ClientSocket.GetStream();
                var listen = new BinaryReader(netStream);
                ClientName = listen.ReadString();
            }
            public void StartClient()
            {
                var clientThread = new Thread(Chat);
                clientThread.Start();
            }
            private void Chat()
            {
                try
                {
                    var netStream = ClientSocket.GetStream();
                    var listen = new BinaryReader(netStream);
                    while (true)
                    {
                        try
                        {
                            var message = listen.ReadString();
                            Server.OnMessageReceived(this, message);
                        }
                        catch (Exception ex)
                        {
                            listen.Close();
                            netStream.Close();
                            Console.WriteLine(ex.Message);
                            return;
                        }
                    }
                }
                finally
                {
                    Server.OnDisconnected(this);
                }
            }
            internal void OnMessageReceived(Client sender, string message)
            {
                var netStream = ClientSocket.GetStream();
                var send = new BinaryWriter(netStream);
                send.Write(sender.ClientName + ": " + message);
            }
        }
    }
    

    and a quick and dirty test client:

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Net;
    using System.Net.Sockets;
    using System.Threading;
    using System.Windows.Forms;
    
    namespace Samples
    {
        static class ChatClient
        {
            [STAThread]
            static void Main()
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                var name = GetChatName();
                if (string.IsNullOrEmpty(name)) return;
                var ipAddress = IPAddress.Parse("127.0.0.1");
                var client = new TcpClient();
                try
                {
                    client.Connect(ipAddress, 8888);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                    return;
                }
                var netStream = client.GetStream();
                var send = new BinaryWriter(netStream);
                send.Write(name);
                var form = new Form { Text = "Chat - " + name };
                var tbSend = new TextBox { Dock = DockStyle.Bottom, Parent = form };
                var tbChat = new TextBox { Dock = DockStyle.Fill, Parent = form, Multiline = true, ReadOnly = true };
                var messages = new List<string>();
                tbSend.KeyPress += (_s, _e) =>
                {
                    if (_e.KeyChar == 13 && !string.IsNullOrWhiteSpace(tbSend.Text))
                    {
                        send.Write(tbSend.Text);
                        tbSend.Text = string.Empty;
                        _e.Handled = true;
                    }
                };
                Action<string> onMessageReceived = message =>
                {
                    if (messages.Count == 100) messages.RemoveAt(0);
                    messages.Add(message);
                    tbChat.Lines = messages.ToArray();
                };
                var listener = new Thread(() =>
                {
                    var listen = new BinaryReader(netStream);
                    while (true)
                    {
                        var message = listen.ReadString();
                        form.BeginInvoke(onMessageReceived, message);
                    }
                });
                listener.IsBackground = true;
                listener.Start();
                Application.Run(form);
            }
            static string GetChatName()
            {
                var form = new Form { Text = "Enter name:", StartPosition = FormStartPosition.CenterScreen };
                var tb = new TextBox { Parent = form, Top = 8, Left = 8, Width = form.ClientSize.Width - 16, Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top };
                var okButton = new Button { Parent = form, Text = "OK", DialogResult = DialogResult.OK, Left = 8 };
                var cancelButon = new Button { Parent = form, Text = "Cancel", Left = okButton.Right + 8 };
                okButton.Top = cancelButon.Top = form.ClientSize.Height - okButton.Height - 8;
                okButton.Anchor = cancelButon.Anchor = AnchorStyles.Bottom | AnchorStyles.Left;
                form.AcceptButton = okButton;
                form.CancelButton = cancelButon;
                var dr = form.ShowDialog();
                return dr == DialogResult.OK ? tb.Text : null;
            }
        }
    }