简体   繁体   中英

How do I send a list of connected clients from the server to the client, differentiating between a regular message?

I am rather new to network programming. I have done tons of googling and research over the past few days and have a chat application that can have multiple users connected to the server and are able to send messages to each other.

Right now there are no catches or methods for a client disconnecting, which I will add at a later date. However right now, I wish to add the functionality of showing a list of online users in a textbox on my client-side form.

When a client connects to the server, the server adds this client to the 'clientList'. However, I am a bit confused on how I would go about sending this list over to the client, but more importantly, how I would make the client recognise that this is not a regular message, more-so a list of clients.

I thought about making it so it sends it with a unique string of characters and doing an if statement, but I know there is a better way of doing it.

On the client-side code, I have a background worker that listens for data from the server. Surely if I serialise the list into a binary formatter, it will be picked up by my 'message listener', and the program will get confused on what is a message and what is data for the connected clients. Therefore I am not sure how I would differentiate between the two.

By no means am I asking you to code for me. I am simply looking for advice from those who have more wisdom and experience in the field. If I could get some pointers on the best way to approach this, I would be more than grateful. I appreciate your time.

Cient side code --

   using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Net.Sockets;
    using System.Net;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;

    namespace socketClientForm
    {
        public partial class Form1 : Form
        {
            private static byte[] buffer = new byte[1024];
            private static Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            public string message = "";
            public Form1()
            {
                InitializeComponent();
                this.Text = "Client";
            }

            delegate void SetTextCallback();

            private void SetText()
            {
                if (this.InvokeRequired)
                {
                    SetTextCallback d = new SetTextCallback(SetText);
                    this.Invoke(d, new object[] { });
                }
                else
                    this.chatBox.AppendText(message);
            }

            private void LoopConnect()
            {
                int attempts = 0;

                while (!clientSocket.Connected)
                    try
                    {
                        attempts++;
                        clientSocket.Connect(IPAddress.Parse(IPBox.Text), 8080);
                    }
                    catch (SocketException)
                    {
                        chatBox.Clear();
                        chatBox.AppendText("Connection attempts: " + attempts.ToString());
                    }

                clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(backgroundWorker1.RunWorkerAsync), clientSocket);
                chatBox.Clear();
                chatBox.AppendText("Connected \n");
            }

            private void submitButton_Click(object sender, EventArgs e)
            {
                if (!String.IsNullOrWhiteSpace(msgBox.Text))
                {

                    string req = usernameBox.Text + ": " + msgBox.Text;
                    byte[] buffer = Encoding.ASCII.GetBytes(req);
                    clientSocket.Send(buffer);
                    msgBox.Text = "";
                }
            }

            private void connectButton_Click(object sender, EventArgs e)
            {
                LoopConnect();
            }

            private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
            {
                while (clientSocket.Connected)
                {
                    try
                    {
                        byte[] receivedBuf = new byte[1024];
                        int rec = clientSocket.Receive(receivedBuf);
                        byte[] data = new byte[rec];
                        Array.Copy(receivedBuf, data, rec);

                        message = Encoding.ASCII.GetString(data) + "\n";
                        SetText();
                    }
                    catch
                    {

                    }
                }
            }
        }
    }

Server side code --

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace socketServer
{
    class Program
    {
        private static byte[] buffer = new byte[1024];
        private static List<Socket> clientSockets = new List<Socket>();
        private static Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        static void Main(string[] args)
        {
            Console.Title = "Server";
            SetupServer();
            Console.ReadLine();
        }

        private static void SetupServer()
        {
            Console.WriteLine("Setting up server...");
            serverSocket.Bind(new IPEndPoint(IPAddress.Any, 8080));
            serverSocket.Listen(1);

            serverSocket.BeginAccept(new AsyncCallback(AcceptCallBack), null);
        }

        private static void AcceptCallBack(IAsyncResult AR)
        {
            Socket socket = serverSocket.EndAccept(AR);
            clientSockets.Add(socket);
            Console.WriteLine("Client Connected");
            socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), socket);
            serverSocket.BeginAccept(new AsyncCallback(AcceptCallBack), null);
        }

        private static void ReceiveCallBack(IAsyncResult AR)
        {
            Socket socket = (Socket)AR.AsyncState;

            int received = socket.EndReceive(AR);
            byte[] dataBuff = new byte[received];
            Array.Copy(buffer, dataBuff, received);

            string text = Encoding.ASCII.GetString(dataBuff);
            Console.WriteLine("Text received: " + text);

            byte[] data = Encoding.ASCII.GetBytes(text);

            foreach (Socket client in clientSockets)
            {
                client.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback(SendCallback), client);
                client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), client);
            }

            //socket.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback(SendCallback), socket);
            //socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), socket);
        }

        private static void SendCallback(IAsyncResult AR)
        {
            Socket socket = (Socket)AR.AsyncState;
            socket.EndSend(AR);
        }
    }
}

It is harder to derive a messaging protocol if you are working with plain bytes and string messages. You are best creating a model - something like

public class NetMessage{

public int MessageType{get;set;}
public dynamic Payload{get;set;}

}

So lets imaging MessageType 1 is your authentication request.

it would be something like

 {    "MessageType":"1",    "PayLoad":{
                 "Username":"Admin",
                 "Password":"Password123"
              }

 }

You can either serialize this to string and send it (via Newtonsoft.Json) Or, as I prefer, using a binary formatter to convert the object to bytes directly and then sending the bytes over the network. Sending the serialized to byte form data, will be just slightly more efficient than sending string info across the network.

Using the protocol described above, you can make your server perform a switch statement on the MessageType and then handle the logic differently.

In your question, you want to send a list of connected clients?

Use something like MessageType 99, and set the Payload to be a List of clients. Just remember, you cannot serialize the TcpClient object and send it to a remote user and expect the object to function like a connected TcpClient. You can at most send the remote ip and the port that the server is connected to. So I would recommend sending a model that represents this data.

UPDATE:

At the moment, your background worker is receiving the data and processing it as byte -> text and then performing straight up business logic on the text.

What you should be using, is managed types, other than the string type. String is too low level, you need some intermediary types to help manage the logic.

Use nuget package manager in visual studio to install Newtonsoft.Json (or JSON.Net it is sometimes known as)

With Newtonsoft you can do the following.

Given a class that looks like this

public class MessageClass
{
    public int MessageType{get;set;}
    public dynamic Payload{get;set;}
}

You can do the following

string content = "{\"MessageType\":\"2\",\"Payload\":\"blah\"}";

This is a JSON formatted string, that represents a class instance.

In C# Code this object would be like this:

var message = new MessageClass();
message.MessageType=2;
message.Payload = "blah";

What Newtonsoft gives you, is the ability to turn strings into managed C# types. Eg:

Remember our string above called 'content' ?

var managedObject = JsonConvert.DeserializeObject<MessageClass>(content);
Console.WriteLine(managedObject.MessageType);  // will; write 2

What I propose, is that your client and server communicate via JSON Formatted objects which then gives you the ability to perform more advanced conditional statements and more accurate assertions.

Newtonsoft provides 2 key methods for you to use. Documented on the newtonsoft website. newtonsoft.com/json/help/html/Methods_T_Newtonsoft_Json_JsonConvert.htm

To turn a C# object into a string JsonConvert.SerializeObject(object o); JsonConvert.DeserializeObject(String value);

For the deserialize method, put the type you are deserializing it to, in where the T is.

Eg:

MessageClass msg = JsonConvert.DeserializeObject<MessageClass>(content);

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