C#.网络编程 Socket基础(四) WPF系统Socket TCP协议 服务器与客户端 不同类型文件传输,解决UI线程(异步委托)与工作线程的卡顿问题

一、简介

虽然,本文的前面几篇文章在WinForm中实现了Socket TCP协议 服务器与客户端 不同类型文件传输,详情见

 C#.网络编程 Socket基础(一)Socket TCP协议 实现端到端(服务器与客户端)简单字符串通信

C#.网络编程 Socket基础(二) 基于WinForm系统Socket TCP协议 实现端到端(服务器与客户端)图片传输

C#.网络编程 Socket基础(三) 基于WinForm系统Socket TCP协议 实现端到端(服务器与客户端).txt.word.png等不同类型文件传输

但是,却没有在WPF中实现 Socket TCP协议 服务器与客户端 不同类型文件传输。因此,本文将描述如何在WPF中实现该功能。

同时,解决UI线程与工作线程的卡顿问题,UI卡顿问题是一个重点,可以参考网页:

https://www.jianshu.com/p/1d19514bccea

https://www.cnblogs.com/TianFang/p/3969430.html

https://blog.csdn.net/u010050735/article/details/79491348

 

二、WPF实现

Server端代码

MainWindow.xaml




    
        
            
                
                    
                    
                
                

                    
                
                    
                    
                    
                    
                
            
        
        

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Data;
using System.Drawing;
//using System.Drawing.Imaging;
using System.IO;
using System.Net;
using System.Net.Sockets;
//using System.Windows.Forms;


namespace SocketServer
{
    /// 
    /// MainWindow.xaml 的交互逻辑
    /// 
    public partial class MainWindow : Window
    {
         public MainWindow()
        {
            InitializeComponent();
            this.TxtBox_ReceivefileStatus.Text = "0/100";
            this.btn_StartServer.Content = "开启器服务器";
        }

        private void btn_StartServer_Click(object sender, RoutedEventArgs e)
        {
            //System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(()=> {
            //this.Dispatcher.Invoke(() =>
            //{

            //    this.TxtBox_ReceivefileStatus.Text = "AA/100";
            //        //this.btn_StartServer.Content = "监听中...";
            //    });
            //}));
            //thread.Start();
            this.btn_StartServer.Content = "监听中...";
            this.TxtBox_ReceivefileStatus.Text = "AA/100";
            System.Threading.Thread thread1 = new System.Threading.Thread(new System.Threading.ThreadStart(() => {
                Socket receiveSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IPEndPoint hostIpEndPoint = new IPEndPoint(IPAddress.Parse("0.0.0.0"), 8888);
                //设置接收数据缓冲区的大小
                byte[] b = new byte[4096];
                receiveSocket.Bind(hostIpEndPoint);
                //监听
                receiveSocket.Listen(2);
                //接受客户端连接
                Socket hostSocket = receiveSocket.Accept();
                //内存流fs的初始容量大小为0,随着数据增长而扩展。
                MemoryStream fs = new MemoryStream();
                //string Path = "C:\\Users\\lanmage2\\Desktop\\AA\\文件1.txt";
                //FileStream fs = new FileStream(Path, FileMode.Open);
                int length = 0;
                //每接受一次,只能读取小于等于缓冲区的大小4096个字节
                while ((length = hostSocket.Receive(b)) > 0)
                {
                    //将接受到的数据b,按长度length放到内存流中。
                    fs.Write(b, 0, length);

                    if (progressBar_Rec.Value < 100)
                    {
                        //进度条的默认值为0
                        progressBar_Rec.Value++;
                        //TxtBox_ReceivefileStatus.Text = "接收:" + progressBar_Rec.Value + "/100";
                    }
                }
                progressBar_Rec.Value = 100;
                // TxtBox_ReceivefileStatus.Text = "接收:" + progressBar_Rec.Value + "/100";
                fs.Flush();

                fs.Seek(0, SeekOrigin.Begin);
                byte[] byteArray = new byte[fs.Length];
                int count = 0;
                while (count < fs.Length)
                {
                    byteArray[count] = Convert.ToByte(fs.ReadByte());
                    count++;
                }
                string Path = "C:\\Users\\lanmage2\\Desktop\\AA";
                //FileStream filestream = new FileStream(Path + "\\文件1.txt", FileMode.OpenOrCreate);
                FileStream filestream = File.Create(Path);
                //filestream.Write(byteArray, 0, byteArray.Length);//不能用
                //System.IO.File.WriteAllBytes(Path, byteArray);//能用

                /*Bitmap类,可以将*/
                //Bitmap Img = new Bitmap(fs);
                //Img.Save(@"reveive.jpg", ImageFormat.Png);
                //关闭写文件流
                fs.Close();
                //关闭接收数据的Socket
                hostSocket.Shutdown(SocketShutdown.Receive);
                hostSocket.Close();
                //关闭发送连接
                receiveSocket.Close();
            }));
            thread1.Start();
            
        }
    }
}

Client 代码:

MainWindow.xaml


    
        
            
                
                    
                    
                
                

                    
                
                    
                    
                    
                    
                    

                
            
        
        

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Net;
using System.Net.Sockets;
using Microsoft.Win32;

namespace SocketClient
{
    /// 
    /// MainWindow.xaml 的交互逻辑
    /// 
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        static Socket sendsocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        //OpenFileDialog表示打开文件对话框。
        OpenFileDialog openFileDialog1 = new OpenFileDialog();
        Byte[] imgByte = new byte[1024];

        /// 
        /// 打开本地文件
        /// 
        /// 
        /// 
        private void btn_OpenFile_Click(object sender, EventArgs e)
        {
            this.btn_OpenFile.Content = "已经打开";
            //可以打开文件的类型
            this.openFileDialog1.Filter = "Image Files(*.BMP;*.JPG;*.GIF;*.PNG)|*.BMP;*.JPG;*.GIF;*.PNG" + "|All Files (*.*)|*.*";
            //如果文件对话框已经打开
            if (this.openFileDialog1.ShowDialog() == true)
            {
                try
                {
                    string path = this.openFileDialog1.FileName;
                    TxtBox_SendFilePath.Text = path;
                    //path表示文件所在的路径。
                    //FileMode.Open表示打开文件。 FileMode.Create表示创建一个文件并覆盖原有的文件。当然还有其他方式。
                    //表示可以读文件(只读)。
                    FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);
                    imgByte = new Byte[fs.Length];
                    //将文件读到imgByte中。
                    fs.Read(imgByte, 0, imgByte.Length);
                    fs.Close();
                }
                catch (Exception)
                {
                }
            }
        }
        /// 
        /// 向服务器发送数据
        /// 
        /// 
        /// 
        private void btn_SendFile_Click(object sender, EventArgs e)
        {
            //实例化socket        
            IPEndPoint ipendpiont = new IPEndPoint(IPAddress.Parse("0.0.0.0"), 8888);
            sendsocket.Connect(ipendpiont);
            MessageBox.Show("服务器IP:" + sendsocket.RemoteEndPoint);
            sendsocket.Send(imgByte);
            sendsocket.Shutdown(System.Net.Sockets.SocketShutdown.Send);
            sendsocket.Close();
            sendsocket.Dispose();
        }

    }
}

三、UI卡顿问题

1、简单的卡顿问题        

在前面的Server MainWindow.xaml.cs,我们会注意到第一个地方,我把工程线程(或耗时线程)放到了 thread1 中去。这样,点击btn_StartServer_Click后,才不会发生阻塞,可以动态更新btn_StartServer的Content(但是,程序运行到 if (progressBar_Rec.Value < 100)   这行会出错。下面会提到UI线程与工作者线程的关系)。如图:

C#.网络编程 Socket基础(四) WPF系统Socket TCP协议 服务器与客户端 不同类型文件传输,解决UI线程(异步委托)与工作线程的卡顿问题_第1张图片

C#.网络编程 Socket基础(四) WPF系统Socket TCP协议 服务器与客户端 不同类型文件传输,解决UI线程(异步委托)与工作线程的卡顿问题_第2张图片

C#.网络编程 Socket基础(四) WPF系统Socket TCP协议 服务器与客户端 不同类型文件传输,解决UI线程(异步委托)与工作线程的卡顿问题_第3张图片

 

若是,都放在主线程,就会发生阻塞,btn_StartServer的Content更新不了,如下:

C#.网络编程 Socket基础(四) WPF系统Socket TCP协议 服务器与客户端 不同类型文件传输,解决UI线程(异步委托)与工作线程的卡顿问题_第4张图片

C#.网络编程 Socket基础(四) WPF系统Socket TCP协议 服务器与客户端 不同类型文件传输,解决UI线程(异步委托)与工作线程的卡顿问题_第5张图片

 

发生阻塞问题的关键所在,是在 Socket hostSocket = receiveSocket.Accept();这一行,因为它一直等待客户端连接,耗时太长,所以UI(即btn_StartServer的Content)不能更新。

四、UI线程无法直接调用工作线程

      你可能会发现,当运行我上面的程序的时候,抛出异常信息   “调用线程无法访问此对象,因为另一个线程拥有该对象”。根本原因是UI线程无法直接在工作线程中使用,WPF中只有UI线程才能操作UI元素,非UI线程要访问UI时就会报异常了。

相关的网页解决方法:

1(网址1)、WPF 工作者线程中,无法直接访问UI线程(比如给UI的控件赋值)http://www.cnblogs.com/527289276qq/p/5264887.html

一般采用如下代码来解决:

//方法一
            this.Dispatcher.Invoke((Action)delegate()
            {
               //你的代码
            }); 

            //方法二
            App.Current.Dispatcher.Invoke((Action)delegate()
            {
                //你的代码
            }); 

2(网址2)

https://blog.csdn.net/conganguo/article/details/73692677

https://www.cnblogs.com/DemonJ/p/6265989.html

System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(delegate
{
    try
    {

    }
    catch (Exception ex)
    {

    }
}));

 

 我采取的办法,采用委托,将代码放到this.Dispatcher.Invoke中,可以动态更新UI,如图

C#.网络编程 Socket基础(四) WPF系统Socket TCP协议 服务器与客户端 不同类型文件传输,解决UI线程(异步委托)与工作线程的卡顿问题_第6张图片

五、效果图

未点击:

C#.网络编程 Socket基础(四) WPF系统Socket TCP协议 服务器与客户端 不同类型文件传输,解决UI线程(异步委托)与工作线程的卡顿问题_第7张图片

 

C#.网络编程 Socket基础(四) WPF系统Socket TCP协议 服务器与客户端 不同类型文件传输,解决UI线程(异步委托)与工作线程的卡顿问题_第8张图片

C#.网络编程 Socket基础(四) WPF系统Socket TCP协议 服务器与客户端 不同类型文件传输,解决UI线程(异步委托)与工作线程的卡顿问题_第9张图片

六,总结

1、解决UI卡顿问题,往往通过创建UI线程、工作线程,将其区分开来。

2、本人邮箱[email protected]

3、IP是自己局域网下本机的IP。

4、this.Dispatcher.BeginInvoke()异步执行,不等待委托结束就更新UIthis.Dispatcher.BeginInvoke()同步执行,需等待委托执行完才更新UI。

5、附录,将服务器端的代码规范后如下:

C#.网络编程 Socket基础(四) WPF系统Socket TCP协议 服务器与客户端 不同类型文件传输,解决UI线程(异步委托)与工作线程的卡顿问题_第10张图片

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace SocketServer
{
    /// 
    /// MainWindow.xaml 的交互逻辑
    /// 
    public partial class MainWindow : Window
    {
         public MainWindow()
        {
            InitializeComponent();
            this.TxtBox_ReceivefileStatus.Text = "0/100";
            this.btn_StartServer.Content = "开启器服务器";
        }

        private void btn_StartServer_Click(object sender, RoutedEventArgs e)
        {
            this.btn_StartServer.Content = "监听中...";
            //点击后改变背景色
            this.btn_StartServer.Background = System.Windows.Media.Brushes.Red;
            Thread receiveThread = new Thread(ReceiveMessage);
            receiveThread.Start();
        }

        private void ReceiveMessage()
        {
            Socket receiveSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint hostIpEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8888);
            //设置接收数据缓冲区的大小
            byte[] b = new byte[4096];
            receiveSocket.Bind(hostIpEndPoint);
            //监听
            receiveSocket.Listen(2);
            //接受客户端连接
            Socket hostSocket = receiveSocket.Accept();
            //内存流fs的初始容量大小为0,随着数据增长而扩展。
            MemoryStream fs = new MemoryStream();
            int length = 0;
            //每接受一次,只能读取小于等于缓冲区的大小4096个字节
            this.Dispatcher.Invoke((Action)delegate ()
               {
                   while ((length = hostSocket.Receive(b)) > 0)
                    {
                        //将接受到的数据b,按长度length放到内存流中。
                        fs.Write(b, 0, length);

                        if (progressBar_Rec.Value < 100)
                        {
                            //进度条的默认值为0
                            progressBar_Rec.Value++;
                            TxtBox_ReceivefileStatus.Text = "接收:" + progressBar_Rec.Value + "/100";
                        }
                    }
                    progressBar_Rec.Value = 100;
                    TxtBox_ReceivefileStatus.Text = "接收:" + progressBar_Rec.Value + "/100";
                }
            );
        
            fs.Flush();
            byte[] byteArray = new byte[fs.Length];
            int count = 0;
            while (count < fs.Length)
            {
                byteArray[count] = Convert.ToByte(fs.ReadByte());
                count++;
            }
            string Path = "C:\\Users\\lanmage2\\Desktop\\AA\\文件1.txt";
            //FileStream filestream = new FileStream(Path + "\\文件1.txt", FileMode.OpenOrCreate);
            //FileStream filestream = File.Create(Path);
            //filestream.Write(byteArray, 0, byteArray.Length);//不能用
            System.IO.File.WriteAllBytes(Path, byteArray);//能用

            /*Bitmap类,可以将*/
            //Bitmap Img = new Bitmap(fs);
            //Img.Save(@"reveive.jpg", ImageFormat.Png);
            //关闭写文件流
            fs.Close();
            //关闭接收数据的Socket
            hostSocket.Shutdown(SocketShutdown.Receive);
            hostSocket.Close();
            //关闭发送连接
            receiveSocket.Close();
        } 
    }
}

 

 

 

 

你可能感兴趣的:(C#.网络编程 Socket基础(四) WPF系统Socket TCP协议 服务器与客户端 不同类型文件传输,解决UI线程(异步委托)与工作线程的卡顿问题)