简体   繁体   中英

C# TCP: Receive data sent to TCPListener from TCPClient

Yesterday I started a project where you can chat with people in a specific chat room in a C# Console Application. There is a TCPListener on the server-side that continuously listens for incoming connections and handles each incoming connection on a separate thread. On the client-side you can connect to this listener by entering an IP address and nickname and you are connected to the TCPListener.

I am able to send messages from Client => Server and the messages arrive at the server, but when multiple clients are connected the other clients won't see my message.

For instance, I have two clients connected to the listener: one with nickname 'user1' and one with nickname 'user2'. When user1 sends a message, the server receives it. When user2 sends a message, the server receives it too. But user1 does not see what user2 sent to the server and vice-versa.

My question

My question is:

How can I make TCPClients connected to the TCPListener receive the messages from other TCPClients?

Additional information

I've added some comments to make it easy to understand the methods I used. The listener listens on port 8345.

Client

    public static void Connect(string serverIP, string nickname)
    {
        try
        {
            TcpClient client = new TcpClient();
            NetworkStream stream = null;
            MessageHelper helper = null;

            client.Connect(serverIP, PORT);
            stream = client.GetStream();
            helper = new MessageHelper(client, stream);
            helper.SendMessage($"!nickname={nickname}");

            Log("Connected!");

            Thread receiveThread = new Thread(new ThreadStart(() =>
            {
                while (true)
                {
                    // Get messages from other senders
                    helper.ReadMessage();
                    Thread.Sleep(300);
                }
            }));

            receiveThread.Start();

            while (Util.IsClientConnected(client))
            {
                Console.Write(" > ");
                var message = Console.ReadLine();

                if (!string.IsNullOrWhiteSpace(message))
                {
                    try
                    {
                        helper.SendMessage(message);
                    }
                    catch (IOException)
                    {
                        Log("The server closed unexpectedly.");
                    }
                }                        
            }

            Log("Disconnected.");
        }
        catch (SocketException ex)
        {
            Log("SocketException: " + ex.ToString());
        }
    }

Server

    public static bool IsListening;
    private const int PORT = 8345;

    public static void HandleClient(object _client)
    {
        TcpClient client = (TcpClient)_client;
        NetworkStream stream = null;
        MessageHelper helper = null;
        var ipAddress = MessageHelper.GetIpInformation(client).IpAddress.ToString();
        stream = client.GetStream();
        helper = new MessageHelper(client, stream);

        // Initial read, assuming this will be a '!nickname' command it will set the nickname
        helper.ReadMessage();
        Log($"{helper.Nickname} ({ipAddress}) connected.");

        while (Util.IsClientConnected(client))
        {
            try
            {
                // Check every 300 ms for a new message and print it to the screen.
                helper.ReadMessage();
            }
            catch (IOException)
            {
                Log($"{helper.Nickname} disconnected.");
            }

            Thread.Sleep(300);
        }
    }

    public static void StartListener()
    {
        TcpListener listener = null;
        IPAddress ipAddress = IPAddress.Any;

        listener = new TcpListener(ipAddress, PORT);
        try
        {
            listener.Start();
            IsListening = true;
            Log($"Listener started at {ipAddress}:{PORT}.");
        }
        catch (Exception ex)
        {
            Log("Error: " + ex.ToString());
        }

        while (IsListening)
        {
            // Check if listener is handling a pending connection, if not, wait 250 ms.
            if (!listener.Pending())
            {
                Thread.Sleep(250);
                continue;
            }

            // Client connected, handle at a separate thread.
            TcpClient client = listener.AcceptTcpClient();
            Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClient));
            clientThread.Start(client);
        }
    }

MessageHelper.cs

public class MessageHelper
{
    public string Nickname { get; set; }
    public TcpClient Client { get; set; }
    public NetworkStream Stream { get; set; }

    public MessageHelper(TcpClient client, NetworkStream stream)
    {
        Client = client;
        Stream = stream;
    }

    public static IpInformation GetIpInformation(TcpClient client, bool isRemote = true)
    {
        string fullHostname;

        if (isRemote)
            fullHostname = client.Client.RemoteEndPoint.ToString();
        else
            fullHostname = client.Client.LocalEndPoint.ToString();

        IpInformation info = new IpInformation()
        {
            IpAddress = IPAddress.Parse(fullHostname.Split(':')[0]),
            Port = int.Parse(fullHostname.Split(':')[1])
        };

        return info;
    }

    public string GetMessageFormat(string message)
    {
        DateTime dateTime = DateTime.Now;
        return $" [{dateTime.ToShortTimeString()}] <{Nickname}>: {message}";
    }

    public void ReadMessage()
    {
        byte[] data = new byte[256];
        int byteCount = Stream.Read(data, 0, data.Length);
        string ipAddress = GetIpInformation(Client).IpAddress.ToString();
        string message = Encoding.ASCII.GetString(data, 0, byteCount);

        // Check if message is a command
        if (message.StartsWith("!"))
        {
            try
            {
                // Command format is >> !command=value <<
                string[] commandSplit = message.TrimStart('!').Split('=');
                string command = commandSplit[0];
                string value = commandSplit[1];

                if (command == "nickname")
                {
                    Nickname = value;
                }
                else
                {
                    Log("This command is not found.");
                }
            }
            catch (Exception)
            {
            }
        }
        else
        {
            // Regular message, print it to the console window.
            Console.ForegroundColor = ConsoleColor.DarkYellow;
            Console.WriteLine(GetMessageFormat(message));
            Console.ResetColor();
        }
    }

    public void SendMessage(string message)
    {
        byte[] data = Encoding.ASCII.GetBytes(message);
        Stream.Write(data, 0, data.Length);

        if (!message.StartsWith("!"))
        {
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine(GetMessageFormat(message));
            Console.ResetColor();
        }
        else
        {
            // Issue the '!nickname' command
            if (message.StartsWith("!nickname"))
            {
                try
                {
                    Nickname = message.TrimStart('!').Split('=')[1];
                }
                catch (IndexOutOfRangeException)
                {
                    Log("Please enter an argument for this command.");
                }
            }
        }
    }
}

In the MessageHelper.cs class ReadMessage method you are not storing the messages or you are not broadcasting the messages to all the clients.

To broadcast

  1. Create an Event in MessageHelper and raise the event at Console.WriteLine(GetMessageFormat(message)); .
  2. Pass the message in EventHandler in the Event.
  3. In Client.cs file, Handle the event to recieve all the messages

Sample Code

In MessageHelper Class declare event as

public event EventHandler ReadMessageEvent;

In ReadMessage Method at Console.WriteLine(GetMessageFormat(message)); replace with

if (ReadMessageEvent!= null)
        ReadMessageEvent.Invoke(GetMessageFormat(message), null);

In Client class try block add below code

helper.ReadMessageEvent += ReadMessageEvent_HandleEvent;

 private void ReadMessageEvent_HandleEvent(object sender, EventArgs e)
 {
   string message = sender as string;
   Console.WriteLine(message);           
 }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM