1. 程式人生 > >C#.網路程式設計 Socket基礎(四) WPF系統Socket TCP協議 伺服器與客戶端 不同型別檔案傳輸,同時解決UI執行緒與工作執行緒的卡頓問題

C#.網路程式設計 Socket基礎(四) WPF系統Socket TCP協議 伺服器與客戶端 不同型別檔案傳輸,同時解決UI執行緒與工作執行緒的卡頓問題

一、簡介

雖然,本文的前面幾篇文章在WinForm中實現了Socket TCP協議 伺服器與客戶端 不同型別檔案傳輸,詳情見

但是,卻沒有在WPF中實現 Socket TCP協議 伺服器與客戶端 不同型別檔案傳輸。因此,本文將描述如何在WPF中實現該功能。

同時,解決UI執行緒與工作執行緒的卡頓問題,UI卡頓問題是一個重點,可以參考網頁:

二、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。如圖:

若是,都放在主執行緒,就會發生阻塞,UI更新不了,如下:

發生阻塞問題的關鍵所在,是在 Socket hostSocket = receiveSocket.Accept();這一行,因為它一直等待客戶端連線,耗時太長,所以UI不能更新。

四,總結

1、解決UI卡頓問題,往往通過建立UI執行緒、工作執行緒,將其區分開來。

2、本人郵箱[email protected]