如何从服务器向客户端发送已连接客户端列表,区分常规消息?

时间:2018-05-02 08:10:18

标签: c# winforms sockets tcp

我对网络编程比较陌生。在过去的几天里,我做了大量的谷歌搜索和研究,并有一个聊天应用程序,可以让多个用户连接到服务器,并能够相互发送消息。

现在没有用于断开客户端的捕获或方法,我将在以后添加。但是现在,我希望在客户端表单的文本框中添加显示在线用户列表的功能。

当客户端连接到服务器时,服务器将此客户端添加到“clientList”。但是,我对如何将此列表发送给客户端有点困惑,但更重要的是,我如何让客户端认识到这不是常规消息,更多是客户列表。

我考虑过这样做,所以它用一个独特的字符串发送它并做一个if语句,但我知道有更好的方法。

在客户端代码上,我有一个后台工作程序,用于侦听来自服务器的数据。当然,如果我将列表序列化为二进制格式化程序,它将被我的“消息监听器”接收,程序将对什么是消息以及连接客户端的数据感到困惑。因此,我不确定如何区分这两者。

我绝不会要求你为我编码。我只是在寻找那些在该领域拥有更多智慧和经验的人的建议。如果我能就最好的方法得到一些指示,我会非常感激。我很感激你的时间。

客户端代码 -

   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
                    {

                    }
                }
            }
        }
    }

服务器端代码 -

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

1 个答案:

答案 0 :(得分:0)

如果使用普通字节和字符串消息,则很难导出消息传递协议。 你最好创建一个模型 - 比如

public class NetMessage{

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

}

因此,让映像MessageType 1成为您的身份验证请求。

就像是

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

 }

您可以将其序列化为字符串并发送(通过Newtonsoft.Json) 或者,我更喜欢使用二进制格式化程序将对象直接转换为字节,然后通过网络发送字节。 发送序列化到字节格式的数据,比通过网络发送字符串信息稍微有效。

使用上述协议,您可以让服务器在MessageType上执行switch语句,然后以不同方式处理逻辑。

在您的问题中,您想发送已连接客户的列表吗?

使用类似MessageType 99的内容,并将Payload设置为客户端列表。 请记住,您无法序列化TcpClient对象并将其发送给远程用户,并期望该对象的功能类似于连接的TcpClient。 您最多可以发送远程IP和服务器连接的端口。 所以我建议发送一个代表这些数据的模型。

更新:

目前,您的后台工作人员正在接收数据并将其作为字节处理 - &gt;文本,然后在文本上直接执行业务逻辑。

您应该使用的是托管类型,而不是字符串类型。 字符串太低,你需要一些中间类型来帮助管理逻辑。

使用visual studio中的nuget包管理器安装Newtonsoft.Json (或JSON.Net有时也称为)

使用Newtonsoft,您可以执行以下操作。

给出一个看起来像这样的类

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

您可以执行以下操作

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

这是一个JSON格式的字符串,表示一个类实例。

在C#代码中,这个对象是这样的:

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

Newtonsoft为您提供的功能是将字符串转换为托管C#类型的能力。 例如:

请记住我们上面的字符串'content'?

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

我建议的是,您的客户端和服务器通过JSON格式化对象进行通信,然后使您能够执行更高级的条件语句和更准确的断言。

Newtonsoft为您提供了两种关键方法。 记录在newtonsoft网站上。 newtonsoft.com/json/help/html/Methods_T_Newtonsoft_Json_JsonConvert.htm

将C#对象转换为字符串 JsonConvert.SerializeObject(object o); JsonConvert.DeserializeObject(String value);

对于deserialize方法,将要反序列化的类型放在T所在的位置。

例如:

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