C#聊天室客户端完整③

窗体

进入聊天室界面(panel里面,label,textbox,button):

聊天界面(flowLayoutPanel(聊天面板)):

文档大纲(panel设置顶层(登录界面),聊天界面在底层)

步骤:设置进入聊天室→输入聊天→右边自己发送的消息→左边别人发的消息

MyClient.cs(进入聊天室类)

internal class MyClient
{
    // 定义委托类型
    public delegate void UpdatLabelHandle(string str = "");
    // 声明委托变量
    public UpdatLabelHandle LabelInfo;
    // 接受和发送消息 创建连接对象写在异步里面
    Thread thConnect; // 连接服务器的线程
    TcpClient client;  // 全局客户端对象
    public bool IsConnect;// 是否连接成功
    Thread receiveThread;// 接收消息的线程
    Thread sendThread;// 发送消息的线程
    Thread updateChatThread;// 更新聊天室ui的线程
    public MyClient() 
    {
        thConnect = new Thread(ConnetServer);
        thConnect.Start();
    }
    public void ConnetServer()
    {
        client = new TcpClient();
        // 开启一个异步的连接
        // 参数1 ip地址
        // 2 端口号
        // 3 回调函数 看一判断是否连接成功
        // 4 传递回调函数的参数
        client.BeginConnect(IPAddress.Parse("192.168.107.72"),3333,requestCallBack,client);
        float num = 0;
        while (IsConnect == false)
        {
            // 证明没有连接成功
            num += 0.1f;
            if (LabelInfo != null) LabelInfo(); // 不传参数的目的
            if (num >= 10f)
            {
                return;//超时连接 10s连接不上就连接失败
            }
            Thread.Sleep(100);
        }
        if (IsConnect==true)// 
        {
            NetworkStream stream = client.GetStream();
            // 在此处开启分线程接收发送消息,更新ui
            sendThread = new Thread(sendHandle);
            sendThread.Start(stream);
            receiveThread = new Thread(receiveHandle);
            receiveThread.Start(stream);
            updateChatThread = new Thread(updateHandle);
            updateChatThread.Start();
        }
    }
    // 把消息保存队列中
    // 可以存储数据结合,先进先出的特点,如果先添加一个你好,你好可以通过方法先取出来
    // Queue 队列 先进先出例如买饭
    // 数组 先进后出的 进电梯
    public Queue<string> SendQueue = new Queue<string>();
    // 发消息
    public void sendHandle(object obj)
    {
        NetworkStream stream = obj as NetworkStream;
        try
        {
            while (IsConnect)
            {
                if (SendQueue.Count>0)// 发短消息不为空 如果把窗体里面发消息文本内容取到此处
                {
                    string msg = SendQueue.Dequeue();// 取出先放进去的数据
                    byte[] bs = Encoding.UTF8.GetBytes(msg);
                    stream.Write(bs,0,bs.Length);
                }
            }
        }
        catch(Exception ex)
        {
            Console.WriteLine("send"+ex.Message);
        }
    }
    public Queue<string> receiveQueue = new Queue<string>();
    // 接受消息
    public void receiveHandle(object obj)
    {
        NetworkStream stream = obj as NetworkStream;
        try
        {
            while (IsConnect)
            {
                byte[] bs = new byte[1024];
                int length = stream.Read(bs, 0, bs.Length);
                string s = Encoding.UTF8.GetString(bs, 0, length);
                receiveQueue.Enqueue(s);
            }
        }
        catch (Exception e)
        {
            Console.WriteLine("receive"+e.Message);
        }
    }
    // 定义委托类型 接受UpdateChatUI方法
    public delegate void updateChatHandle(string s, bool a = false);
    // 定义委托变脸
    public updateChatHandle F1;
    // 更新ui
    public void updateHandle()
    {
        while (true)
        {
            if (F1!=null&& receiveQueue.Count>0)
            {
                F1(receiveQueue.Dequeue(), false);
            }
        }
    }
    // IAsyncResult 异步结果的类
    // BeginConnect 的回调函数 不管成功与否都执行
    public void requestCallBack(IAsyncResult ar)
    {
        TcpClient t = ar.AsyncState as TcpClient;// 通过AsyncState异步状态属性获取参数
        if (t.Connected) // 如果连接成功了
        {
            IsConnect = true;
            LabelInfo("连接成功");
            t.EndConnect(ar); // 结束挂起的状态 
        }
        else
        {
            //  连接失败 
            LabelInfo("连接失败");
        }
        LabelInfo = null;
    }
    public void Stop()
    {
        if (IsConnect)
        {
            IsConnect = false;
            if (client!=null)
            {
                client.Close();
                client = null;
            }
            // 把线程终端
            if (thConnect!=null)
            {
                thConnect.Abort();// 终止线程
            }
            if (sendThread != null)
            {
                sendThread.Abort();
            }
            if (receiveThread != null)
            {
                receiveThread.Abort();
            }
            if (updateChatThread!= null)
            {
                updateChatThread.Abort();
            }
        }
    }
}

ItemRight.cs(右边信息类)

public class ItemLeft:Panel
{
    // 聊天气泡 label和圆形的头像
    // 消息内容 和父窗体的宽度
    public ItemLeft(string msg, int parentWidth)
    {
        this.Font = new Font("楷体", 18);
        // 设置气泡宽度
        this.Width = parentWidth - 20 - 6;
        PictureBox pic = new PictureBox();
        pic.Image = Image.FromFile("D:\\李克课件\\Csharp\\网络通信\\6.13聊天室服务器\\02 聊天室客户端\\大爱仙尊.jpg");
        pic.Width = 60;
        pic.Height = 60;
        pic.SizeMode = PictureBoxSizeMode.StretchImage;// 把图片压缩以适应盒子大小
        pic.Location = new Point(10, 10);// 右边头像的位置
        // 设置头像圆形 通过绘制绘制圆形
        GraphicsPath gp = new GraphicsPath();// 创建一个绘制线对象
        gp.AddEllipse(pic.ClientRectangle);
        Region re = new Region(gp);// 绘制的椭圆生成一个区域图片
        pic.Region = re;// 把区域图片赋值给pic
        this.Controls.Add(pic);
        // 绘制label
        // 计算msg宽度和高度
        Graphics g = this.CreateGraphics();// 创建绘制对象
        int exceptWidth = this.Width - 200; // 期望宽度
        // MeasureString 测量指定这个字符串的长度或者宽度
        // 参数1 测量的字符串
        // 2 指定字符串
        // 3 一行盼望的宽度
        float width = g.MeasureString(msg, this.Font, exceptWidth).Width;
        float height = g.MeasureString(msg, this.Font, exceptWidth).Height;
        Label l = new Label();
        l.Text = msg;
        l.BackColor = Color.Green;
        l.Location = new Point(80, 10);
        l.Width = (int)width;
        l.Height = (int)height;
        this.Controls.Add(l);
        // 更新panel的高度
        if ((int)height + 20 < 80)
        {
            this.Height = 80;
        }
        else
        {
            this.Height = (int)height + 20;
        }
        //re.Dispose();// 释放资源
        //gp.Dispose();
    }   
}

ItemRight.cs(左边聊天框)

    // 聊天气泡 label和圆形的头像
    // 消息内容 和父窗体的宽度
    public ItemLeft(string msg, int parentWidth)
    {
        this.Font = new Font("楷体", 18);
        // 设置气泡宽度
        this.Width = parentWidth - 20 - 6;
        PictureBox pic = new PictureBox();
        pic.Image = Image.FromFile("D:\\李克课件\\Csharp\\网络通信\\6.13聊天室服务器\\02 聊天室客户端\\大爱仙尊.jpg");
        pic.Width = 60;
        pic.Height = 60;
        pic.SizeMode = PictureBoxSizeMode.StretchImage;// 把图片压缩以适应盒子大小
        pic.Location = new Point(10, 10);// 右边头像的位置
        // 设置头像圆形 通过绘制绘制圆形
        GraphicsPath gp = new GraphicsPath();// 创建一个绘制线对象
        gp.AddEllipse(pic.ClientRectangle);
        Region re = new Region(gp);// 绘制的椭圆生成一个区域图片
        pic.Region = re;// 把区域图片赋值给pic
        this.Controls.Add(pic);
        // 绘制label
        // 计算msg宽度和高度
        Graphics g = this.CreateGraphics();// 创建绘制对象
        int exceptWidth = this.Width - 200; // 期望宽度
        // MeasureString 测量指定这个字符串的长度或者宽度
        // 参数1 测量的字符串
        // 2 指定字符串
        // 3 一行盼望的宽度
        float width = g.MeasureString(msg, this.Font, exceptWidth).Width;
        float height = g.MeasureString(msg, this.Font, exceptWidth).Height;
        Label l = new Label();
        l.Text = msg;
        l.BackColor = Color.Green;
        l.Location = new Point(80, 10);
        l.Width = (int)width;
        l.Height = (int)height;
        this.Controls.Add(l);
        // 更新panel的高度
        if ((int)height + 20 < 80)
        {
            this.Height = 80;
        }
        else
        {
            this.Height = (int)height + 20;
        }
        //re.Dispose();// 释放资源
        //gp.Dispose();
    }   
}

窗体代码

public partial class Form1 : Form
{
    Timer timer;// 定时器
    bool isRunning = false;// 开关
    MyClient client;
    public Form1()
    {
        InitializeComponent();
        this.flowLayoutPanel1.AutoScroll = true;
        timer = new Timer()
        {
            Interval = 100, // 时间间隔
        };
        timer.Tick += (send, arg) =>
        {
            isRunning = false;
            timer.Stop();
        };
    }
    // 进入聊天室按钮方法
    private void button1_Click(object sender, EventArgs e)
    {
        if (!string.IsNullOrEmpty(textBox1.Text))
        {
            // 开始连接服务器 封装一个自定义客户端类
            client = new MyClient(); 
            // 给client委托赋值updateLabel
            client.LabelInfo = updateLabel;
            client.F1 = UpdateChatUI;// 把方法赋值给f1变量
        }
        else
        {
            MessageBox.Show("请输入你的名字");
        }
    }
    public List<string> list1 = new List<string>() { "拼命加载中", "拼命加载中.", "拼命加载中..", "拼命加载中..." };
    int index = 0;
     // 封装一个更新label的方法
    public void updateLabel(string str)
    {
        this.Invoke((Action)(() =>
        {
            if (string.IsNullOrEmpty(str))// 正在连接中
            {
                label1.Text = list1[index];
                index++;
                if (index == list1.Count) index = 0;
            }
            else // 证明连接有结果时候
            {
                this.label1.Text = str;
                // 需要判断如果连接成功了 需要进入聊天室
                if (client.IsConnect)
                {
                    // 登录成功 现实聊天界面null
                    this.Controls.Remove(this.panel1);
                    this.Text = this.textBox1.Text;// 修改窗体标题
                }
            }
        }));
    }
    // 发送消息的按钮的方法
    // 1 给服务器发送消息,封装MyClient.cs文件中
    // 2 更新聊天界面,封装到form1.cs文件中,如果MyClient.cs需要使用把封装更新UI传递过去
    // 使用委托
    // 3 聊天界面 自定义控件区分到底是谁的消息
    private void button2_Click(object sender, EventArgs e)
    {
        if (isRunning)
        {
            return;
        }
        isRunning = true;
        timer.Start();

        Console.WriteLine("111");
        string msg = this.textBox2.Text.Trim();
        if (msg.Length != 0)
        {
            // 开始服务器发送消息 封装MyClient.cs文件中
            msg = this.Text + "说:" + msg;
            // 把msg发送队列
            client.SendQueue.Enqueue(msg);//添加数据到队列里面
            // 更新UI
            UpdateChatUI(msg,true);
            // 再次输入
            this.textBox2.Text = "";
        }
    }
    // 展示聊天室
    // 参数1 是消息内容
    // 参数2 是否是自己发的消息
    public void UpdateChatUI(string msg,bool isSelf)
    {
        this.Invoke((Action)(() =>
        {
            Panel item = null;
            if (isSelf) // 显示在右边
            {
                item = new ItemRight(msg, flowLayoutPanel1.Width);
            }
            else // 别人发的消息 显示左边
            {
                item = new ItemLeft(msg, flowLayoutPanel1.Width);
            }
            // 显示在flowlayoutpanel上,
            this.flowLayoutPanel1.Controls.Add(item);
            // 让flowLayoutPanel1 滚动到最下面
            this.flowLayoutPanel1.VerticalScroll.Value = this.flowLayoutPanel1.VerticalScroll.Maximum;
        }));
    }
    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        if (client != null)
        {
            client.Stop();
            client = null;
        }
    }
    // 进入聊天室
    private void textBox1_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.KeyCode ==Keys.Enter)
        {
            // 点击了Ennter键
            button1_Click(null, null);
        }
    }
    private void textBox2_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.KeyCode == Keys.Enter)
        {
            // 点击了Ennter键
            button2_Click(null, null);
        }
    }
}

天才是百分之九十九的汗水加百分之一的灵感

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/717464.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

轮式机器人Swiss-Mile城市机动性大提升:强化学习引领未来城市物流

喜好儿小斥候消息&#xff0c;苏黎世联邦理工学院的研究团队成功开发了一款革命性的机器人控制系统&#xff0c;该系统采用强化学习技术&#xff0c;使轮式四足机器人在城市环境中的机动性和速度得到了显著提升。 喜好儿网 这款专为轮腿四足动物设计的控制系统&#xff0c;能…

crmeb Pro版/多店版商城付费会员、会员卡功能说明

一、功能介绍 用户开通付费会员后&#xff0c;可获得多项商城优惠&#xff0c;商家可通过此功能锁定重要客户&#xff0c;培养客户消费习惯等 二、操作流程 用户 &#xff1e; 会员管理 &#xff1e; 付费会员 三、功能说明 1. 会员类型 付费卡类型&#xff1a;月卡、季卡…

【电机控制】FOC算法验证步骤——PWM、ADC

【电机控制】FOC算法验证步骤 文章目录 前言一、PWM——不接电机1、PWMA-H-50%2、PWMB-H-25%3、PWMC-H-0%4、PWMA-L-50%5、PWMB-L-75%6、PWMC-L-100% 二、ADC——不接电机1.电流零点稳定性、ADC读取的OFFSET2.电流钳准备3.运放电路分析1.电路OFFSET2.AOP3.采样电路的采样值范围…

RNN-循环神经网络

1.前者的输出作为后者的输入&#xff08;循环&#xff09;&#xff0c;有先后关系的信息&#xff0c;前影响后&#xff0c;时间序列。处理数字信息。 2.独热编码 ont-hot Encoding&#xff1a;处理的文字数据地位相同&#xff0c;相当于用二进制数01给数据编码&#xff0c;会增…

学习笔记——网络管理与运维——SNMP(SNMP原理)

四、SNMP原理 SNMP的工作原理基于客户端-服务器模型。其中&#xff0c;网络管理系统是客户端&#xff0c;而网络设备是服务器。客户端向服务器发送请求消息(即"Get"或"Set"命令)来获取或修改服务器的信息。服务器收到请求消息后&#xff0c;会返回相应的响…

使用volta管理前端开发环境

背景&#xff1a;公司有新老不同的产品&#xff0c;使用的node版本不一样&#xff0c;每次都要手动切换node版本&#xff0c;对应的项目才能运行。这样很麻烦&#xff0c;有没有好的解决方法&#xff0c;就找到了volta。 1.为什么是volta&#xff1f; 管网介绍&#xff1a;使用…

中文翻译藏语的软件都有哪些?分享3款实用的!

在数字化时代&#xff0c;语言不再是沟通的障碍。随着科技的飞速发展&#xff0c;中文翻译藏语的软件层出不穷&#xff0c;为那些对藏族文化感兴趣或需要在藏区工作、旅行的人们提供了极大的便利。本文将为您盘点几款热门的中文翻译藏语软件&#xff0c;助您轻松跨越语言鸿沟。…

AMEYA360代理:纳芯微NSOPA240x系列破解旋转变压器之“难”

随着市场对高精度、高性能电机控制技术的不断追求&#xff0c;旋转变压器作为其核心部件之一&#xff0c;其精确测量角度位置和转速的能力显得尤为重要。 然而&#xff0c;旋转变压器驱动电路的特殊要求一直是行业发展的技术瓶颈。为解决这一挑战&#xff0c;纳芯微近日发布了全…

抖音a_bogus,mstoken全参数爬虫逆向补环境2024-06-15最新版

抖音a_bogus,mstoken全参数爬虫逆向补环境2024-06-15最新版 接口及参数 打开网页版抖音&#xff0c;右键视频进入详情页。F12打开控制台筛选detail&#xff0c;然后刷新网页&#xff0c;找到请求。可以发现我们本次的参数目标a_bogus。a_bogus有时长度为168有时为172&#xf…

Node.js安装扫盲

一、Node.js安装 在官网下载node.js安装包 双击打开node-v20.14.0-x64.ms文件&#xff0c;点击运行 进入安装Node.js的对话框&#xff0c;点击Next继续 勾选复选框后点击Next继续 默认安装路径 默认配置 这里不需要勾选&#xff0c;直接点击Next 点击Install 二、Node.js验…

PyQt5和Eric7的安装使用 —— Python篇

需要安装Python的朋友请看另一篇文章&#xff1a; windows系统安装Python -----并安装使用Pycharm编辑器 一、安装PyQt5&#xff1a; 1、方法一&#xff1a;使用pip命令在线安装。 输入以下命令可以直接安装&#xff1a; pip install PyQt5 由于安装默认使用国外的镜像&a…

【免费Web系列】大家好 ,今天是Web课程的第二二天点赞收藏关注,持续更新作品 !

这是Web第一天的课程大家可以传送过去学习 http://t.csdnimg.cn/K547r 员工管理 1. 修改员工 对于修改功能&#xff0c;分为两步实现&#xff1a; 点击 “编辑” 根据ID查询员工的信息&#xff0c;回显展示。 点击 “保存” 按钮&#xff0c;修改员工的信息 。 1.1 回显…

行业透视 | ERP系统成熟度评判:五个关键能力解析-亿发

在现代企业管理中&#xff0c;ERP系统&#xff08;企业资源计划系统&#xff09;已成为不可或缺的工具。然而&#xff0c;什么样的ERP系统才算是成熟的&#xff1f;以下几个关键能力&#xff0c;是一个成熟的ERP系统所必备的&#xff0c;缺一不可。 数据一体化&#xff0c;远离…

RT-Thread简介及启动流程分析

阅读引言&#xff1a; 最近在学习RT-Thread的内部机制&#xff0c;觉得这个启动流程和一些底层原理还是挺重要的&#xff0c; 所以写下此文。 目录 1&#xff0c; RT-Thread简介 2&#xff0c;RT-Thread任务的几种状态 3&#xff0c; 学习资源推荐 4&#xff0c; 启动流程分…

[element-ui]el-select多选选择器选中其中一个选项,不可删除

背景&#xff1a; 产品真的很多奇奇怪怪的需求&#xff0c;一边吐槽一边实现。 前提&#xff1a;选择器作为表格的筛选项&#xff0c;提供三个选项值。 要求&#xff1a;默认选中其中一个值&#xff0c;这个值不可删除。 如图&#xff1a; 小声吐槽&#xff1a;搞这些有什么…

Mobaxterm 配置 ssh 隧道

背景介绍&#xff1a; 在使用 ssh远程 连接服务器时&#xff0c;由于许多服务器并没有公网ip&#xff0c;或者不能从内部直接访问&#xff0c;经常使用 跳板机端口转发 的形式访问服务器。 但是在实际使用中&#xff0c;我们经常会有些网络和数据交换操作&#xff0c;需要用到…

【刷题】LeetCode刷题汇总

目录 一、刷题题号1&#xff1a;两数之和 二、解法总结1. 嵌套循环2. 双指针 一、刷题 记录LeetCode力扣刷题 题号1&#xff1a;两数之和 双循环&#xff08;暴力解法&#xff09;&#xff1a; class Solution {public int[] twoSum(int[] nums, int target) {int[] listne…

仪表运放输入端抗RFI滤波器设计注意事项

1 概述 有个潜在问题却往往被忽视&#xff0c;即仪表放大器中存在的射频整流问题。当存在强射频干扰时&#xff0c;集成电路的内部结点可能对干扰进行整流&#xff0c;然后以直流输出失调误差表现出来&#xff1b; 2 共模和差模输入滤波器 该滤波器针对CM(R1-C1和R2-C2)&#…

快去复习吧+++常用算法及参考算法 递推法++穷举法++排序(冒泡、选择)++查找(顺序、折半)++字符串处理++方程求根++无穷级数求和

接上&#xff1a;常用算法及参考算法 &#xff08;1&#xff09;累加 &#xff08;2&#xff09;累乘 &#xff08;3&#xff09;素数 &#xff08;4&#xff09;最大公约数 &#xff08;5&#xff09;最值问题 &#xff08;6&#xff09;迭代法 常用算法及参考算法 7. 递推法…

vite配置之获取.env.[mode]下的数据

需求 vite.config.ts获取配置文件下面的数据.vue,.ts,.tsxsrc文件夹下面获取配置文件下面的数据 一、src/* .vue,.ts,.tsx 文件夹下面使用环境变量 之前webpack或者用的vue-cli我们在获取配置文件数据的时候通过process.env&#xff0c;但是在vite里面不能通过这种方式 vit…