zoukankan      html  css  js  c++  java
  • 网络编程之即时通信程序(聊天室)(四)客户端搭建

           上一节我们搭建了即时通信程序的登录端,这一节我们要实现即时通信程序的主客户端的搭建,也就是聊天、发文件端的创建。讲完这一节之后,我们就可以自己实现一个即时通信程序了。好了,先上一个图。 

     

    UI布局如下:有一个ListBox用来显示当前在线用户命名为onLineList

                   三个文本框分别为:txtchatContenttxtsendMsgtxtsendFile,分别表示:聊天的记录、发送信息框、要发送的文件框

                   四个按钮分别为:btnsendMsgbtnsendAllbutton1button2,分别表示:

    发送消息、群发消息、选择要发送的文件、发送文件

    下面我们通过具体的示例来向大家一步步的进行讲解。

    首先需要创建以下全局变量:

           

            private ClientLogin clientLogin;//用来存储从ClientLogin传过来的client参数
            private string ClientName;//表示当前的用户名
            private TcpClient tcpClient;//全局的TcpClient对象,用来负责客户端的连接、通信
            private NetworkStream netStream;//全局的NetworkStream对象,负责发送、接受信息
    

    接着,我们需要在构造函数里做如下处理:

    public ChatClient(ClientLogin clogin)
            {
                InitializeComponent();
                TextBox.CheckForIllegalCrossThreadCalls = false;//取消跨线程检测,允许非UI线程操作UI元素
                skinEngine1.SkinFile = "SteelBlack.ssk";//加载皮肤文件 
                clientLogin = clogin;//将从登陆框传入的参数赋值给clientLogin
                lbname.Text += clientLogin.LoginName;
                tcpClient = clogin.TCPClient;将从登陆框传入的clogin的TCPClient属性赋值给全局tcpClient,负责客户端的通信
                if (tcpClient != null)
                {
                    netStream = tcpClient.GetStream();
                    txtchatContent.Text = "连接成功!\r\n";
                    Thread listenThread = new Thread(new ThreadStart(Listening));//专门启动一个线程负责与服务端沟通
                    listenThread.Start();
                }
    

    我们在Listening函数中来处理具体监听操作,我们在第一节中已经定制了客户端的通信,在Listening函数里有用到,在这里我们再温习一下。

    1、先判断接收到的第一个字节是否为0,如果为0则直接保存为文件,如果不为0,则进行如下的判断。

    2、接收到的第一个字节不为0,即收到的信息为字符串。

    2.1、如果接收到的字符串格式为“OnLine|登录用户名|”,则显示登录用户名

    2.2、如果接受到的字符串格式为“Off|用户名1、用户名2、用户名3.....|”,如果有用户登录或者离开则更新在线用户列表

    2.3、如果收到的为其他格式的字符串,则直接添加在聊天窗口里,显示聊天信息。

     public void Listening()
            {
                int length=0;
                while (true)
                {   //先创建一个10M的缓存区
                    byte[] Msg = new byte[1024 * 1024 * 10];
                    //将传输的流读取到该缓冲区中
                    length=netStream.Read(Msg, 0, Msg.Length);
                    //如果缓冲区的第一字节为0则为文件,否则,则为消息 
                    if (Msg[0] == 0)
                    {      //直接新建一个文件流保存传输过来的文件
                        SaveFileDialog sfDialog = new SaveFileDialog();
                        if (sfDialog.ShowDialog() == DialogResult.OK)
                        {
                            FileStream fs = new FileStream(sfDialog.FileName, FileMode.OpenOrCreate, FileAccess.Write);
                            fs.Write(Msg, 1, length - 1);
                            MessageBox.Show("文件传输完毕!");
                        }
                    }
                    else
                    {
                        //第一个字节不为0,肯定为消息字符串
                        string msg = Encoding.Default.GetString(Msg);
                        string[] results = msg.Split(new char[] { '|' });
                        if (results[0]=="OnLine")//如果有人上线,则显示上线人姓名
                        {
                            txtchatContent.Text += results[1]+"\r\n";
                        }
                        else if (results[0] == "Off")//有人上线或离开,更新在线人员列表
                        {
                            string[] clients = results[1].Split(new char[] { '、' });
                            onlineList.Items.Clear();
                            for (int i = 0; i < clients.Length-1; i++)
                            {
                                onlineList.Items.Add(clients[i]);       
                            }
                        }
                        else
                        {
                            txtchatContent.Text += msg+"\r\n";
                        }
                    }
                }
            }
    

     接受我们需要在btnsendMsg按钮的Click事件的处理函数做如下处理:

    需要将发送的消息做特殊的处理,发送信息的格式为:MSG|接受者姓名|发送者姓名|发送内容|

    在发送信息前,要选择要发送的用户,如果没有选择用户则不能进行发送信息。

    private void btnsendMsg_Click(object sender, EventArgs e)
            {
                if (onlineList.SelectedItem!=null)
                {
                    if (!string.IsNullOrEmpty(txtsendMsg.Text))
                    {
                        try
                        {
                            string msg ="MSG|"+ onlineList.SelectedItem.ToString() + "|"+clientLogin.LoginName+"|" + txtsendMsg.Text+"\r\n|";
                            byte[] buffer = Encoding.Default.GetBytes(msg);
                            this.netStream.Write(buffer, 0, buffer.Length);
                        }
                        catch (Exception e2)
                        {
                            MessageBox.Show("信息发送异常:" + e2.Message);
                        }
                    }
                    else
                    {
                        MessageBox.Show("要发送的信息不能为空!");
                    }
                }
                else
                {
                    MessageBox.Show("请选择要发送的对象!");
                }
            }
    

    如果我们需群发信息,则直接点击群发信息按钮就可以了,所以我们在btnSendAll按钮中做如下处理,将需要群发的信息进行加工,信息格式为:MSG|发送者姓名|发送内容|

     

            private void btnsendAll_Click(object sender, EventArgs e)
            {
                string msg = "MSGALL|"+clientLogin.LoginName+"|" + txtsendMsg.Text+"\r\n|";
                byte[] buffer = Encoding.Default.GetBytes(msg);
                this.netStream.Write(buffer, 0, buffer.Length);
            }
    

     如果我们需要发送文件,则需要先选择要发送的文件,所以在button1Click事件的处理函数中做如下处理,选择要发送的文件(文件大小不能超过10M

          private void button1_Click(object sender, EventArgs e)
            {
                OpenFileDialog ofDialog = new OpenFileDialog();
                if (ofDialog.ShowDialog() == DialogResult.OK)
                {
                   FileInfo f = new FileInfo(ofDialog.FileName);
                    if (f.Length < 1024 * 1224 * 10)
                    {
                        txtsendFile.Text = ofDialog.FileName;
                    }
                    else
                    {
                        MessageBox.Show("不好意思!您传输的文件大于10M,请分割后再传!");
                    }
    

      选择文件后就可以点击发送,在button2Click事件的处理函数中做如下处理,将要发送的文件读取到字节数组中,但是需要将该字节数组的首字节设置为0,然后才进行发送。

    具体代码如下:

    private void button2_Click(object sender, EventArgs e)
            {
                byte[] buffer = new byte[1024 * 1024 * 10];
                try
                {
                    FileStream fs = new FileStream(txtsendFile.Text, FileMode.Open, FileAccess.Read);
                    int length = fs.Read(buffer, 0, buffer.Length);
                    byte[] buffers=new byte[length+1];
                    buffers[0] = 0;
                    //将bytes里的数据从第0个开始拷贝到filetype里,从第一个位置开始,一共拷贝length个数据
                    Buffer.BlockCopy(buffer, 0, buffers, 1, length);
                    this.netStream.Write(buffers, 0, buffers.Length);
                    FileStream fsq = new FileStream(@"D:\1.txt", FileMode.Create, FileAccess.Write);
                    fsq.Write(buffers, 0,length+1);
                    fsq.Close();
                }
                catch (Exception e2)
                {
                    MessageBox.Show("文件传输异常:" + e2.Message);
                }
            }   
                }
            }
    

    好了到这里我们已经完成了整个即时通信程序的设计和实现,希望可以让大家对.NET平台下网络通信有新的认识,也希望能够对大家有所帮助。这个通信程序里我们主要用到的知识要点有TCP/UDP 通信(TCPListener/TCPClient)Socket套接字、多线程、文件的操作等。如果有对以上知识点不清楚的朋友可以参看我以前写的相关的文章,希望可以对大家有所帮助。好了这一系列的文章就到这里了。

     

     

     

  • 相关阅读:
    解决CentOS 7 history命令不显示操作记录的时间和用户身份问题
    CentOS7关闭selinux
    Centos7下添加开机自启动服务和脚本
    快速查看一个文件的权限 stat -c %a
    修改centos7系统语言
    sudo
    chsh命令 修改用户登录shell
    忘记root开机密码及怎样开启密码远程连接模式
    centos7系统中添加 pstree 命令
    vim 多行添加注释,取消注释
  • 原文地址:https://www.cnblogs.com/Olive116/p/2734411.html
Copyright © 2011-2022 走看看