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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值