WinForm通信:聊天室(简单)

发布于:2025-04-10 ⋅ 阅读:(36) ⋅ 点赞:(0)

使用界面的效果

服务端界面

客户端登录界面

登录成功界面

登录第二个服务端

最终效果

首先了解socket客户端和socket服务端

socket客户端:

        1.创建Socket对象,绑定IP,构建链接

        2.发送send

        3.接收数据Receive,注意,需要持续性接收,加While,异步操作Task,取消CancellationTokenSource

        4.注意,有链接就应该有断开 

socket服务端:

        1.创建socket对象,绑定IP,启用监听

        2.监听对象的获取使用Accept方法

        3.推送消息的方法跟客户端相同都是Send

搭建聊天室的思路

1.如何区分昵称是否重复(登录的账号需要排重)

2.只要是消息都会被服务器接收到,如何区分消息类型?(登录,消息,退出)

3.如何在列表中选择对应的好友进行发送

直接开始敲代码

(作者太懒了,wimform界面自己看着效果图去搭建)

搭建服务端

搭建服务端之前先创建一个User类用来存储登录的用户列表,当然也可以使用别的方式(数据库等)来进行存储
 internal class User
 {
     public string NickName { get; set; } // 昵称
     public Socket Client { get; set; }// 客户端Socket
 }
开始搭建服务端
 public partial class Form1 : Form
 {
     private Socket socketServer = null; // 伺服器端Socket
     private CancellationTokenSource cts = null; // 取消Token
     private List<User> clients = new List<User>(); // 用来存储登录的用户列表
     public Form1()
     {
         InitializeComponent();
     }
     #region 启动和关闭服务器
     private void button1_Click(object sender, EventArgs e)
     {
         try
         {
             if (button1.Text == "启动服务器")
             {
                 StartServer();
             }
             else if (button1.Text == "关闭服务器")
             {
                 CloseServer();
             }
         }
         catch (Exception ex)
         {
             MessageBox.Show(ex.Message, "错误提示", MessageBoxButtons.OK, MessageBoxIcon.Stop);
             return;
         }
     }

     /// <summary>
     /// 启动服务器
     /// </summary>
     private void StartServer()
     {
         socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
         // 客户端是链接,服务器是监听
         socketServer.Bind(new IPEndPoint(IPAddress.Parse(textBox1.Text), int.Parse(textBox2.Text)));
         socketServer.Listen(100);

         button1.Text = "关闭服务器";
         textBox1.Enabled = false;
         textBox2.Enabled = false;
         //等待客户端连接
         cts = new CancellationTokenSource();
         Task.Run(async () =>
         {
             while (!cts.IsCancellationRequested)
             {
                 //async与await等价于new Action
                 //Socket属于套字节,在服务器中相当于一个监听器(TCPListener),在客户端相当于一个连接器
                 Socket socketClient = await socketServer.AcceptAsync();
                 //处理客户端请求
                 ProcessClient(socketClient);
             }
         }, cts.Token);
     }

     /// <summary>
     /// 关闭服务器
     /// </summary>
     private void CloseServer()
     {
         if (socketServer != null)
         {
             if (clients.Count > 0)
             {
                 //发通知
                 BroadCastMessage("通知:服务器关闭,请退出聊天室");
             }
             cts.Cancel();
             socketServer.Close();
             socketServer = null;
             button1.Text = "启动服务器";
             textBox1.Enabled = true;
             textBox2.Enabled = true;
         }
     }
     #endregion
     #region 处理客户端请求(a:登录 b:消息)
     /// <summary>
     /// 处理客户端请求(a:登录 b:消息)
     /// </summary>
     /// <param name="socketClient"></param>
     private void ProcessClient(Socket socketClient)
     {
         Task.Run(() =>
         {
             while (socketClient.Connected)
             {
                 byte[] buffer = new byte[socketClient.Available];
                 int len = socketClient.Receive(buffer);
                 if (len > 0)
                 {
                     //a|||内容
                     //a
                     //内容
                     string[] messages = Encoding.UTF8.GetString(buffer).Split(new string[] { "|||" }, StringSplitOptions.RemoveEmptyEntries);
                     if (messages[0] == "a")
                     {
                         //登录>>>判断用户是否存在
                         if (clients.FindIndex((u) => u.NickName == messages[1]) != -1)
                         {
                             //昵称重复
                             socketClient.Send(Encoding.UTF8.GetBytes("昵称重复"));
                         }
                         else
                         {
                             //昵称不重复.将用户添加到列表
                             clients.Add(new User()
                             {
                                 NickName = messages[1],
                                 Client = socketClient
                             });
                             //整理客户端需要的消息列表
                             UserOnLine(clients);
                         }
                     }
                     else if (messages[0] == "c")
                     {
                         //有用户下线了,发通知
                         int index = clients.FindIndex((u) => u.NickName == messages[1]);
                         if (index != -1)
                         {
                             clients.RemoveAt(index);
                             BroadCastMessage("通知:\"" + messages[1] + "\"下线了");
                         }
                         //发通知刷新列表
                         UserOnLine(clients);
                     }
                     else if (messages[0] == "b")
                     {
                         //需要找两个对象
                         //1.找到发送者
                         Socket socket1 = clients.Find((u) => u.NickName == messages[1]).Client;
                         //2.找到接收者
                         Socket socket2 = clients.Find((u) => u.NickName == messages[2]).Client;
                         //确保接收者在线
                         if (socket2 != null && socket2.Connected)
                         {
                             //对于客户端来说,客户端.Send就代表向自己的客户端上推一条消息
                             //接收者收到消息
                             socket2.Send(Encoding.UTF8.GetBytes($"{messages[1]}:{messages[3]}"));
                             //发送者收到消息
                             socket1.Send(Encoding.UTF8.GetBytes($"{messages[1]}:{messages[3]}"));
                         }
                     }
                     else if (messages[0] == "d")
                     {
                         // 群发消息
                         Socket socket1 = clients.Find((u) => u.NickName == messages[1]).Client;
                         BroadCastMessage(messages[1] + "(群发):" + messages[2]);
                     }
                 }
             }
         });
     }
     #endregion
     #region 消息列表
     private void UserOnLine(List<User> clients)
     {
         string str = string.Empty;
         for (int i = 0; i < clients.Count; i++)
         {
             //把所有的在线用户昵称拼接成一个字符串,发送给客户端
             str += clients[i].NickName + "||||";
         }
         BroadCastMessage(str);
     }
     /// <summary>
     /// 广播消息的方法
     /// </summary>
     /// <param name="message"></param>
     private void BroadCastMessage(string message)
     {
         //有多少个客户就发送多少条广播
         for (int i = 0; i < clients.Count; i++)
         {
             Socket sockClirnt = clients[i].Client;
             if (sockClirnt.Connected)
             {
                 sockClirnt.Send(Encoding.UTF8.GetBytes(message));
             }
         }
     }
     #endregion

     #region 点击发送按钮会给全部客户端发送通知
     private void button2_Click(object sender, EventArgs e)
     {
         if (socketServer == null)
         {
             MessageBox.Show("服务器未启动", "错误提示", MessageBoxButtons.OK, MessageBoxIcon.Stop);
             return;
         }
         if (clients.Count == 0)
         {
             MessageBox.Show("没有在线用户", "错误提示", MessageBoxButtons.OK, MessageBoxIcon.Stop);
             return;
         }
         if (string.IsNullOrWhiteSpace(richTextBox1.Text))
         {
             MessageBox.Show("消息不能为空", "错误提示", MessageBoxButtons.OK, MessageBoxIcon.Stop);
             return;
         }

         BroadCastMessage("通知:" + richTextBox1.Text);
         richTextBox1.Text = string.Empty;
     }
     #endregion
     /// <summary>
     /// 退出程序时关闭服务器
     /// </summary>
     /// <param name="sender"></param>
     /// <param name="e"></param>
     private void Form1_FormClosing(object sender, FormClosingEventArgs e)
     {
         CloseServer();
     }
 }

服务端搭建完成,开始搭建客户端

(客户端需要两个Form界面,作者这里的Form2界面是登录界面,Form1界面是登录成功后的聊天室界面)

在搭建好Form2和Form1界面之后,需要对应用程序的主入口点的代码进行修改,也就是Program.cs中的代码

 static void Main()
 {
     Application.EnableVisualStyles();
     Application.SetCompatibleTextRenderingDefault(false);
     //主要是用于跳转界面
     //当点击登录并成功登录后,会跳转到聊天界面
     Form2 form2 = new Form2();
     if (form2.ShowDialog() == DialogResult.OK)
     {
         Application.Run(new Form1());
     }
 }
开始操作登录界面的代码
 public partial class Form2 : Form
 {
     //一个对象只能链接一次 但是一个程序 .可以使用exe启动多次

     //使用静态是为了在Form1中更简便的使用
     public static Socket socketClient = null; // 客户端Socket
     public static string NickName { get; set; } // 昵称
     // 用来存储登录的用户列表
     public static string[] OnLineUsers { get; set; }
     public Form2()
     {
         InitializeComponent();
         // 为静态的客户端对象初始化
         socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
     }
     private async void button1_Click(object sender, EventArgs e)
     {
         if (string.IsNullOrEmpty(textBox1.Text))
         {
             MessageBox.Show("昵称不可为空");
             return;
         }
         //连接服务器
         try
         {
             socketClient.Connect(new IPEndPoint(IPAddress.Parse(textBox2.Text), int.Parse(textBox3.Text)));
             //发送消息,告诉服务器,有客户端进来了,方便服务器处理
             //a:用来表示登录
             socketClient.Send(Encoding.UTF8.GetBytes($"a|||{textBox1.Text}"));
             //接收消息
             string msg=await ReceiveMessage();
             if(msg=="昵称重复了")
             {
                 MessageBox.Show("昵称重复了,请重新输入","错误提示",MessageBoxButtons.OK,MessageBoxIcon.Stop);
                 textBox1.Clear();
                 textBox1.Focus();
                 return;
             }
             else
             {
                 //跳转聊天>>>获取到在线用户有哪些
                 string[] users = msg.Split(new string[] { "||||" }, StringSplitOptions.RemoveEmptyEntries);
                 //排除自身
                 OnLineUsers = users.Where(u => u != textBox1.Text).ToArray();
             }

         }
         catch (SocketException ex)
         {
             MessageBox.Show($"网络错误:{ex.Message}", "错误提示", MessageBoxButtons.OK, MessageBoxIcon.Stop);
             return;
         }
         catch (Exception ex)
         {
             MessageBox.Show($"发生错误:{ex.Message}","错误提示",MessageBoxButtons.OK,MessageBoxIcon.Stop);
             return;
         }
         //如果连接成功,那么就关闭当前窗口,跳转到聊天窗口
         NickName = textBox1.Text;
         DialogResult = DialogResult.OK;
     }
     #region 接受消息处理昵称重复的问题
     private Task<string> ReceiveMessage()
     {
         return Task.Run(() =>
         {
             while (true)
             {

                 //只要构建链接成功,那么socketClient不关闭的情况下始终都会来链接,所以一旦服务端有消息回复,这边会收到.return出来结束掉while
                 byte[] buffer = new byte[socketClient.Available];
                 int len = socketClient.Receive(buffer);
                 if (len > 0)
                 {
                     return Encoding.UTF8.GetString(buffer);
                 }
             }
         });
     }

     #endregion

 }
开始对聊天室界面的代码进行编写
public partial class Form1 : Form
{

    CancellationTokenSource cts = new CancellationTokenSource(); // 取消Token
    public Form1()
    {
        InitializeComponent();
        this.Text = Form2.NickName;
        if (Form2.OnLineUsers != null && Form2.OnLineUsers.Length > 0)
        {
            this.listBox1.DataSource = Form2.OnLineUsers;
        }
        else
        {
            this.listBox1.DataSource = new string[] { "暂无在线用户" };
        }
        //this.listBox1.DataSource = Form2.OnLineUsers;
        //等待其他用户发送消息
        Task.Run(() =>
        {
            while (!cts.IsCancellationRequested)
            {
                byte[] buffer = new byte[Form2.socketClient.Available];
                int len = Form2.socketClient.Receive(buffer);
                if (len > 0)
                {
                    string msg = Encoding.UTF8.GetString(buffer);
                    //处理该消息是不是广播的在线用户
                    if (msg.Contains("||||"))
                    {
                        string selectedUser = null;
                        if (listBox1.SelectedItem != null)
                        {
                            //获取当前选中的用户
                            selectedUser = listBox1.SelectedItem.ToString();
                        }
                        //重新绑定列表
                        string[] users = msg.Split(new string[] { "||||" }, StringSplitOptions.RemoveEmptyEntries);
                        listBox1.DataSource = null;
                        listBox1.DataSource = users.Where(u => u != Form2.NickName).ToArray();
                        if (selectedUser != null)
                        {
                            listBox1.SelectedItem = selectedUser;
                        }
                    }
                    else
                    {
                        //展示消息
                        Invoke(new Action(() =>
                        {
                            if (msg.StartsWith(Form2.NickName))
                            {
                                //自己的内容在右侧
                                richTextBox1.SelectionAlignment = HorizontalAlignment.Right;
                                richTextBox1.SelectionRightIndent = 2;
                                richTextBox1.SelectionBackColor = Color.FromArgb(0, 153, 255);
                                richTextBox1.SelectionColor = Color.White;
                                richTextBox1.AppendText(msg + "\r\n\r\n");
                            }
                            else
                            {
                                //其他用户
                                richTextBox1.SelectionAlignment = HorizontalAlignment.Left;
                                richTextBox1.SelectionRightIndent = 2;
                                if (msg.StartsWith("通知"))
                                {
                                    richTextBox1.SelectionColor = Color.White;
                                    richTextBox1.SelectionColor = Color.Red;
                                    listBox1.DataSource = null;
                                }
                                else
                                {
                                    richTextBox1.SelectionBackColor = Color.White;
                                    richTextBox1.SelectionColor = Color.Black;
                                }
                                richTextBox1.AppendText(msg + "\r\n\r\n");
                            }
                        }));
                    }
                }
            }
        },cts.Token);
    }
    /// <summary>
    /// 关闭窗口时,关闭socketClient
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        if (Form2.socketClient.Connected)
        {
            //发送消息,告诉服务器,有客户端退出了
            Form2.socketClient.Send(Encoding.UTF8.GetBytes($"c|||{Form2.NickName}"));
            cts.Cancel();//取消任务
            Form2.socketClient.Disconnect(false);
            Form2.socketClient.Close();
            Form2.socketClient = null;
        }
    }
    /// <summary>
    /// 发送消息
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void button1_Click(object sender, EventArgs e)
    {
        if(checkBox1.Checked)
        {
            //如果是广播
            if (string.IsNullOrWhiteSpace(richTextBox2.Text))
            {
                MessageBox.Show("发送内容不能为空", "错误提示", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                return;
            }
            if (Form2.socketClient.Connected)
            {
                //聊天:自己,内容
                Form2.socketClient.Send(Encoding.UTF8.GetBytes($"d|||{Form2.NickName}|||{richTextBox2.Text}"));
            }
        }
        else
        {
            //如果是单聊
            if (listBox1.SelectedItem == null)
            {
                MessageBox.Show("请选择发送用户", "错误提示", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                return;
            }
            if (string.IsNullOrWhiteSpace(richTextBox2.Text))
            {
                MessageBox.Show("发送内容不能为空", "错误提示", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                return;
            }
            if (Form2.socketClient.Connected)
            {
                //聊天:自己,朋友,内容
                Form2.socketClient.Send(Encoding.UTF8.GetBytes($"b|||{Form2.NickName}|||{listBox1.SelectedItem}|||{richTextBox2.Text}"));
            }
        }
       
        richTextBox2.Text = string.Empty;
    }
}

以上是简易聊天室的全部代码,作者只是提供一个思路,如有不足之处请各位大佬批评指正!!!

                                                                                                                                    --HH牛码