alentor
alentor

Reputation: 27

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());
            }
        }
    }
}

Upvotes: 1

Views: 2428

Answers (2)

Ivan Stoev
Ivan Stoev

Reputation: 205629

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;
        }
    }
}

Upvotes: 4

Mike Nakis
Mike Nakis

Reputation: 61993

All your clients are blocked on var message = Console.ReadLine(); so none of them proceeds further down to see if an incoming message has arrived.

You will have to find a way to use asynchronous I/O for reading from the console. Still, an incoming message may arrive while the user is typing, in which case the typed text will be garbled together with the incoming message on the console. A console is not a very good user interface for a chat application.

Upvotes: 5

Related Questions