C#.網路程式設計 Socket基礎(四) WPF系統Socket TCP協議 伺服器與客戶端 不同型別檔案傳輸,同時解決UI執行緒與工作執行緒的卡頓問題
阿新 • • 發佈:2018-12-18
一、簡介
雖然,本文的前面幾篇文章在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]