一、简介
虽然,本文的前面几篇文章在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。如图:
若是,都放在主线程,就会发生阻塞,UI更新不了,如下:
发生阻塞问题的关键所在,是在 Socket hostSocket = receiveSocket.Accept();这一行,因为它一直等待客户端连接,耗时太长,所以UI不能更新。
四,总结
1、解决UI卡顿问题,往往通过创建UI线程、工作线程,将其区分开来。
2、本人邮箱[email protected]