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

时间:2024-04-07 22:02:16

一、简介

虽然,本文的前面几篇文章在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

<Window x:Class="SocketServer.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:SocketServer"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">


    <Grid>
        <Border BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Left" Height="231" Margin="9,33,0,0" VerticalAlignment="Top" Width="500">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition  Width="1*"/>
                    <ColumnDefinition  Width="3*"/>
                </Grid.ColumnDefinitions>
                <Canvas Grid.Column="0" Background="#FF8383BC">

                    <Label x:Name="Lbl_ReceivefileStatus" Content="接收文件的状态" HorizontalAlignment="Left" Height="31" VerticalAlignment="Top" Width="101" Canvas.Left="14" Canvas.Top="157"/>
                    <Label x:Name="Lbl_ReceivefileName" Content="接收文件的名字" HorizontalAlignment="Left" Height="25" VerticalAlignment="Top" Width="101" Canvas.Left="14" Canvas.Top="28"/>
                    <Label x:Name="Lbl_ReceivefileType" Content="接收文件的类型" HorizontalAlignment="Left" Height="27" VerticalAlignment="Top" Width="101" Canvas.Left="14" Canvas.Top="87"/>

                </Canvas>
                <Canvas Grid.Column="1" Background="Blue">
                    <TextBox Name="TxtBox_ReceivefileStatus"  HorizontalAlignment="Left" Height="22" VerticalAlignment="Top" Width="60" Canvas.Left="300" Canvas.Top="159"/>
                    <TextBox x:Name="TxtBox_ReceivefileName"  HorizontalAlignment="Left" Height="22" VerticalAlignment="Top" Width="352" Canvas.Left="8" Canvas.Top="28"/>
                    <TextBox x:Name="TxtBox_ReceivefileType" HorizontalAlignment="Left" Height="22" VerticalAlignment="Top" Width="352" Canvas.Left="8" Canvas.Top="87"/>
                    <ProgressBar x:Name="progressBar_Rec" Height="22" Width="285" Canvas.Left="9" Canvas.Top="159" RenderTransformOrigin="0.5,0.5" Background="#FF00FFCB"/>
                </Canvas>
            </Grid>
        </Border>
        <Button x:Name="btn_StartServer" Content="aaaa" HorizontalAlignment="Left" Height="22" Margin="193,283,0,0" VerticalAlignment="Top" Width="146" Background="Silver" Click="btn_StartServer_Click"/>
    </Grid>

</Window>

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
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    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";
                //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();
            
        }
    }
}

效果图:

 

三、UI卡顿问题

        在前面的Server MainWindow.xaml.cs,我们会注意到第一个地方,我把工程线程(或耗时线程)放到了 thread1 中去。这样,点击btn_StartServer_Click后,才不会发射阻塞,可以动态更新UI。如图:

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

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

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

 

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

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

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

 

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

四,总结

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

2、本人邮箱[email protected]