.NET基础-17-Socket通信与多线程

Socket通信与多线程

服务器端


public Form1()
        {
            InitializeComponent();

            //在程序加载的时候取消跨线程的检测
            Control.CheckForIllegalCrossThreadCalls = false;
        }

        /// <summary>
        /// 开始监听
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStart_Click(object sender, EventArgs e)
        {
            try
            {
                //IP地址(InterNetwork表示IPV4)/使用TCP就是使用数据流/使用的流式的Socket,对应的为Tcp协议
                Socket sockWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //创建IP对象
                IPAddress ip = IPAddress.Parse(this.txtServer.Text);
                //直接获取本机的IP地址
                //IPAddress ip = IPAddress.Any;
                //创建端口号对象
                IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(this.txtPort.Text));
                //绑定IP/端口号信息
                sockWatch.Bind(point);
                ShowMessage("监听开始......");
                //设置监听队列,限定最大连接数,到达最大连接数的时候需要排队
                sockWatch.Listen(10);
                //创建一个新的线程来持续的监听
                Thread th1 = new Thread(Listen);
                th1.IsBackground = true;
                th1.Start(sockWatch);
            }
            catch
            {

            }
        }

        /// <summary>
        /// 保存客户端连接时创建的用来通信的Socket的集合
        /// </summary>
        Dictionary<string, Socket> SocketSend = new Dictionary<string, Socket>();

        /// <summary>
        /// 负责监听
        /// </summary>
        /// <param name="obj"></param>
        private void Listen(object obj)
        {
            Socket socketWatch = obj as Socket;
            //循环监听
            while (true)
            {
                try
                {
                    //负责监听的Socket,用来接收客户端的连接,并且创建负责通信的Socket
                    //等待客户端的连接
                    //这里使用循环来监听,并且放到线程中,防止卡死主线程
                    Socket sockSend = socketWatch.Accept();
                    ShowMessage(sockSend.RemoteEndPoint.ToString() + "连接成功......");
                    //添加到下拉列表列表中
                    cboUsers.Items.Add(sockSend.RemoteEndPoint.ToString());
                    //添加到字典中
                    SocketSend.Add(sockSend.RemoteEndPoint.ToString(), sockSend);
                    //开启一个线程来不断的接收客户端发送的信息,传入的参数为当前新增的Socket线程
                    Thread th2 = new Thread(Rec);
                    th2.IsBackground = true;
                    th2.Start(sockSend);
                }
                catch
                {

                }
            }
        }

        /// <summary>
        /// 不断的接收客户端发送的信息
        /// </summary>
        /// <param name="obj"></param>
        private void Rec(object obj)
        {
            Socket sockSend = obj as Socket;

            while (true)
            {
                try
                {
                    //创建一个字符数组用来接收客户端发送的信息
                    byte[] buffer = new byte[1024 * 1024 * 5];
                    //接收客户端发送的信息
                    int r = sockSend.Receive(buffer);
                    //如果接收到的信息为空,说明客户端下线了
                    if (r == 0)
                    {
                        break;
                    }
                    //解析接收到的数组信息
                    string str = System.Text.Encoding.UTF8.GetString(buffer, 0, r);
                    ShowMessage(sockSend.ReceiveTimeout.ToString() + ":" + str);
                }
                catch
                {

                }
            }
        }

        /// <summary>
        /// 界面信息
        /// </summary>
        /// <param name="str"></param>
        public void ShowMessage(string str)
        {
            txtLog.AppendText(str + "\r\n");
        }

        /// <summary>
        /// 发送信息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSend_Click(object sender, EventArgs e)
        {
            //把文本文件转换成字节数组
            byte[] buffer = System.Text.Encoding.UTF8.GetBytes(this.txtMsg.Text);
            //创建协议,来确定传输的是什么数据
            List<byte> list = new List<byte>();
            //增加一个协议规则(这里需要处理,这样太简单)
            list.Add(0);
            list.AddRange(buffer);
            //重新转换为一个字节数组
            byte[] newBuffer = list.ToArray();
            //发送的客户端对象不能为空
            if (this.cboUsers.SelectedItem == null || this.cboUsers.SelectedItem.ToString() == "")
            {
                MessageBox.Show("请选择客户信息");
                return;
            }
            //获取选中的客户端
            string ip = this.cboUsers.SelectedItem.ToString();
            //发送信息
            SocketSend[ip].Send(newBuffer);
        }

        /// <summary>
        /// 选择文件,并把文件路径显示在文本框中
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSelect_Click(object sender, EventArgs e)
        {
            //创建一个打开文本框的对象
            OpenFileDialog opf = new OpenFileDialog();
            //设置文本框的提示信息
            opf.Title = "请选择需要发送的文件";
            //设置默认打开文件件
            opf.InitialDirectory = @"C:\Users\Administrator\Desktop\test";
            //设置选择文件类型
            opf.Filter = "图片|*.jpg|视频|*.mav|所有文件|*.*";
            opf.ShowDialog();
            //把获取的路径放到文本框里
            this.txtPath.Text = opf.FileName;
        }

        /// <summary>
        /// 发送文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSendFile_Click(object sender, EventArgs e)
        {
            //获取需要发送的文件路径
            string path = this.txtPath.Text.ToString();
            //创建一个数据流的对象
            FileStream fsRead = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Read);
            //创建一个字节数组来接收读取的信息
            byte[] buffer = new byte[fsRead.Length];
            int r = fsRead.Read(buffer, 0, buffer.Length);
            //释放流的资源
            fsRead.Dispose();
            fsRead.Close();
            //创建协议对数据进行处理
            List<byte> list = new List<byte>();
            list.Add(1);
            list.AddRange(buffer);
            byte[] newBuffer = list.ToArray();
            //发送的客户端对象不能为空
            if (this.cboUsers.SelectedItem == null || this.cboUsers.SelectedItem.ToString() == "")
            {
                MessageBox.Show("请选择客户信息");
                return;
            }
            //获取客户端IP地址
            string ip = this.cboUsers.SelectedItem.ToString();
            //发送信息
            SocketSend[ip].Send(newBuffer);
        }

        /// <summary>
        /// 震荡
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnZD_Click(object sender, EventArgs e)
        {
            //创建一个字节数组,只有一个元素,保存表示震动的协议
            byte[] buffer = new byte[1];
            buffer[0] = 2;
            //发送的客户端对象不能为空
            if (this.cboUsers.SelectedItem == null || this.cboUsers.SelectedItem.ToString() == "")
            {
                MessageBox.Show("请选择客户信息");
                return;
            }
            //获取客户端IP地址
            string ip = this.cboUsers.SelectedItem.ToString();
            //发送信息
            SocketSend[ip].Send(buffer);
        }



客户端



public Form1()
        {
            InitializeComponent();

            //取消跨线程检测
            Control.CheckForIllegalCrossThreadCalls = false;
        }

        /// <summary>
        /// 创建通信用线程
        /// </summary>
        Socket socketSend;

        /// <summary>
        /// 连接服务器
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStart_Click(object sender, EventArgs e)
        {
            //创建通信用的Socket
            socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //获取IP地址
            IPAddress ip = IPAddress.Parse(this.txtServer.Text);
            //获取端口号
            IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(this.txtPort.Text));
            //连接服务器
            socketSend.Connect(point);
            ShowMessage("连接服务器成功......");

            //创建线程来接收服务端发送信息
            Thread th1 = new Thread(Rec);
            //设置为后台线程
            th1.IsBackground = true;
            th1.Start();
        }

        /// <summary>
        /// 一直循环接收服务器发送的信息
        /// </summary>
        private void Rec()
        {
            while (true)
            {
                try
                {
                    //创建一个字节数组来接收服务器端发送的信息
                    byte[] buffer = new byte[1024 * 1024 * 5];
                    //接收服务器发送的信息
                    int r = socketSend.Receive(buffer);
                    //获取协议信息
                    byte b = buffer[0];

                    //根据协议解析接收到的信息
                    //说明发送的是文本
                    if (b == 0)
                    {
                        //把接收到的字节数组转换成字符串
                        string str = System.Text.Encoding.UTF8.GetString(buffer, 1, r - 1);
                        ShowMessage(str);

                    }
                    //说明发送的是文件
                    else if (b == 1)
                    {
                        //创建一个保存文本框
                        SaveFileDialog sfd = new SaveFileDialog();
                        //设置文本框的提示信息
                        sfd.Title = "请选择文件的保存路径";
                        //设置文件的保存类型
                        sfd.Filter = "图片|*.jpg|视频|*.mav|所有文件|*.*";
                        //设置文件保存的初始路径
                        sfd.InitialDirectory = @"C:\Users\Administrator\Desktop\test";
                        sfd.ShowDialog();

                        string path = sfd.FileName;
                        //创建流来保存数据
                        FileStream fsWrite = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write);
                        fsWrite.Write(buffer, 1, r - 1);
                        ShowMessage("保存文件成功");
                    }
                    //说明发送的是震动
                    else if (b == 2)
                    {
                        ZD();
                    }
                }
                catch
                {

                }
            }
        }

        /// <summary>
        /// 震动
        /// </summary>
        private void ZD()
        {
            //这里应该先保存窗体当前的位置
            //然后把节点增加n,减少n,重复n次
            //之后把窗体还原到原先的位置
            for (int i = 0; i < 200; i++)
            {
                this.Location = new Point(220, 220);
                this.Location = new Point(420, 420);
            }
        }

        /// <summary>
        /// 界面显示信息
        /// </summary>
        /// <param name="p"></param>
        private void ShowMessage(string str)
        {
            this.txtLog.AppendText(str + "\r\n");
        }

        /// <summary>
        /// 发送信息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSend_Click(object sender, EventArgs e)
        {
            //需要发送的信息
            string str = this.txtMsg.Text;
            //将需要发送的数据转换为字节数组
            byte[] buffer = System.Text.Encoding.UTF8.GetBytes(str);
            //发送信息到服务端
            socketSend.Send(buffer);
        }



PS:

客户端没有与其他客户端通信的功能

客户端没有发送文件的功能

协议这里只是很简单的堆文件和文本只做了区别,没有对文件格式做区分,这个需要完善协议才可以

这里咩有对大文件进行处理,大文件发送涉及断点重传的问题,会比较麻烦

在此基础上可以做个聊天小程序





猜你喜欢

转载自blog.csdn.net/m0_37532448/article/details/80321573