今天介绍一下如何使用hslcommuniation实现方便的网络框架。虽然之前我也写了相关的文章,但是比较零散,没有进行综合的比较。今天的文章将结合一些实际情况来说明。
开始之前先介绍下:hslcommunication 官网:http://www.hslcommunication.cn/
在Visual Studio 中的NuGet管理器中可以下载安装,也可以直接在NuGet控制台输入下面的指令安装:
1
|
Install-Package HslCommunication |
如果需要教程:Nuget安装教程:http://www.cnblogs.com/dathlin/p/7705014.html
组件的完整信息和API介绍参照:http://www.cnblogs.com/dathlin/p/7703805.html 组件的使用限制,更新日志,都在该页面里面。
本库的demo源代码地址:https://github.com/dathlin/HslCommunication
情景一:
我有一个数据服务器,服务器会定时更新这个数据,然后所有需要这个数据的客户端都可以同时获取到数据信息,进行相关的界面更新。我们称这种机制为发布-订阅机制。客户端向服务器订阅自己需要的数据,服务器更新了这个数据就向所有订阅了的客户端发布最新的数据。
这种模式就特别适合用来做一些客户端界面的实时数据的展示。比如我一个客户端的界面,用来显示设备的实时数据,只有服务器才有资格或是条件访问设备。那么就特别合适用发布订阅。
发布-订阅模式最典型的就是MQTT协议,而HSL支持了MQTT协议的3.1.1版本,我们现在来开发一个程序看看。
先新建一个服务器的控制台程序,和客户端的winform程序。接下来是安装nuget组件hslcommunication。打开nuget
我们来点击安装核心的网络通信组件。
我们搜索这个组件,然后勾选安装的项目后,点击进行安装组件。
然后我们在服务器端写点代码,我们现在要推送从0,1,2,3,4,5,6。 我们随便定义一个主题的名字,“A”好了,然后每秒推送一次,程序很短
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using HslCommunication; using HslCommunication.MQTT; namespace Server { class Program { static void Main( string[] args ) { MqttServer server = new MqttServer( ); server.ServerStart( 1883 ); int i = 0; while(true) { System.Threading.Thread.Sleep( 1000 ); server.PublishTopicPayload( "A", Encoding.UTF8.GetBytes( i.ToString( ) ) ); i++; } } } }
我们然后在客户端的界面上放一个label,用来显示收到的数据信息。
我们然后在窗体启动的方法里,进行启动网络。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using HslCommunication; using HslCommunication.MQTT; namespace WindowsFormsApp1 { public partial class Form1 : Form { public Form1( ) { InitializeComponent( ); } private void Form1_Load( object sender, EventArgs e ) { client = new MqttClient( new MqttConnectionOptions( ) { IpAddress = "127.0.0.1", Port = 1883, } ); // 接收到数据的时候进行触发 client.OnMqttMessageReceived += Client_OnMqttMessageReceived; // 订阅服务器的主题,在连接成功后就去订阅 client.OnClientConnected += m => { m.SubscribeMessage( "A" ); }; client.ConnectServer( ); } private void Client_OnMqttMessageReceived( string topic, byte[] payload ) { // 切回主线程进行显示文本 Invoke( new Action( ( ) => { label1.Text = Encoding.UTF8.GetString( payload ); } ) ); } private MqttClient client; } }
然后先启动服务器,我们在启动客户端,就可以看到一个数字在更新。
当然了,这可能没有什么,如果你这时候,打开多个客户端,比如打开个8个客户端。
你就会发现所有的数据都是同时更新的。如果是更多的客户端也没事的。这时候可能又有朋友会问了,你这只传递了一个数据啊,我一个界面的数据很多啊,可能几十个,上百个呢。怎么传递
这个问题的核心,如何把你的所有的需要传递的数据给压缩到一个byte[]里面,然后显示的时候,进行解析成你需要的数据,这个过程就是序列化和反序列化。
事实上,这个没有标准的答案,我下面给出2种思路。
第一种思路是,使用json技术,把需要的数据编程变成一个字符串,然后转byte[],我们假设有3个label的数据需要显示。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using HslCommunication; using HslCommunication.MQTT; using Newtonsoft.Json.Linq; namespace Server { class Program { static void Main( string[] args ) { MqttServer server = new MqttServer( ); server.ServerStart( 1883 ); int i = 0; while(true) { System.Threading.Thread.Sleep( 1000 ); JObject json = new JObject( ); json.Add( "温度1", new JValue( i ) ); json.Add( "温度2", new JValue( i + 1 ) ); json.Add( "温度3", new JValue( i + 2 ) ); server.PublishTopicPayload( "A", Encoding.UTF8.GetBytes( json.ToString( ) ) ); i++; } } } }
我们用了json组件的技术,只要using就可以开始使用了。然后我们在客户端也需要进行修改。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using HslCommunication; using HslCommunication.MQTT; using Newtonsoft.Json.Linq; namespace WindowsFormsApp1 { public partial class Form1 : Form { public Form1( ) { InitializeComponent( ); } private void Form1_Load( object sender, EventArgs e ) { client = new MqttClient( new MqttConnectionOptions( ) { IpAddress = "127.0.0.1", Port = 1883, } ); // 接收到数据的时候进行触发 client.OnMqttMessageReceived += Client_OnMqttMessageReceived; // 订阅服务器的主题,在连接成功后就去订阅 client.OnClientConnected += m => { m.SubscribeMessage( "A" ); }; client.ConnectServer( ); } private void Client_OnMqttMessageReceived( string topic, byte[] payload ) { // 切回主线程进行显示文本 Invoke( new Action( ( ) => { JObject json = JObject.Parse( Encoding.UTF8.GetString( payload ) ); label1.Text = json.Value<int>( "温度1" ).ToString( ); label2.Text = json.Value<int>( "温度2" ).ToString( ); label3.Text = json.Value<int>( "温度3" ).ToString( ); } ) ); } private MqttClient client; } }
你可以对照一下看看,主要就是修改了显示部分的内容,
好了,现在可以同时发多个数据了,当然了,如果是数组,也可以的,无非是json的序列化,和反序列化,你对json组件理解越深刻,你就可以实现更高级的功能了。
我们再来看看另一个xml技术,其实本质上来说,和json是一样的,都是封装数据,解析数据。此处我就贴个关键的代码,实现就靠你们自己了。
服务器端:
客户端的代码:
我们发现,就是压缩和解析的地方不一样而已,思路都是差不多的,性能上来说,也是几乎没有什么差别的。上面举例了复杂数据的传递,我们再来看看另一种经典的情况,应该怎么处理。
我们除了form1,还有个子窗体form2,子窗体也需要显示一个主题的信息。
当我们点击form1上的按钮时,form2才显示出来。好了,我们现在有两个主题了,“A”和“B”,我们修改下服务器的内容
static void Main( string[] args ) { MqttServer server = new MqttServer( ); server.ServerStart( 1883 ); int i = 0; while(true) { System.Threading.Thread.Sleep( 1000 ); XElement element = new XElement( "Data" ); element.SetAttributeValue( "温度1", i ); element.SetAttributeValue( "温度2", i + 1 ); element.SetAttributeValue( "温度3", i + 2 ); server.PublishTopicPayload( "A", Encoding.UTF8.GetBytes( element.ToString( ) ) ); server.PublishTopicPayload( "B", Encoding.UTF8.GetBytes( i.ToString( ) ) ); i++; } }
主要就是增加了发送B主题的数据信息。
form1窗体的那个显示form2的按钮的事件如下:
private void button1_Click( object sender, EventArgs e ) { using(Form2 form = new Form2( )) { form.ShowDialog( ); } }
我们在form2里应该怎么写订阅呢。
using HslCommunication.MQTT; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApp1 { public partial class Form2 : Form { public Form2( ) { InitializeComponent( ); } private void Form2_Load( object sender, EventArgs e ) { client = new MqttClient( new MqttConnectionOptions( ) { IpAddress = "127.0.0.1", Port = 1883, } ); // 接收到数据的时候进行触发 client.OnMqttMessageReceived += Client_OnMqttMessageReceived; // 订阅服务器的主题,在连接成功后就去订阅 client.OnClientConnected += m => { m.SubscribeMessage( "B" ); }; client.ConnectServer( ); } private void Client_OnMqttMessageReceived( string topic, byte[] payload ) { // 切回主线程进行显示文本 Invoke( new Action( ( ) => { label1.Text = Encoding.UTF8.GetString( payload ); } ) ); } private MqttClient client; private void Form2_FormClosing( object sender, FormClosingEventArgs e ) { client.ConnectClose( ); } } }
写法上来说,和form1是一样的,但是有个小细节需要注意,form2的窗体在关闭的时候,需要关闭和服务器的连接,不然后台依然连接的网络,导致刷新界面的时候,界面已经销毁了,然后发送奔溃。
一般来说是需要再实例化一个MqttClient对象的,然后还是会有人咨询,我能不能所有的界面用一个MqttClient对象,不然我界面一多,不就实例化很多了么。
要解决这个问题,确实不太容易。涉及到一个概念,事件的绑定和解绑操作。好的,我们再来看看,如果实现这个需求。我们先修改下form1的代码
private void Client_OnMqttMessageReceived( string topic, byte[] payload ) { // 切回主线程进行显示文本 Invoke( new Action( ( ) => { // 我的form1只对A进行了订阅,按照道理应该只对A进行响应操作。 if (topic == "A") { XElement element = XElement.Parse( Encoding.UTF8.GetString( payload ) ); label1.Text = element.Attribute( "温度1" ).Value; label2.Text = element.Attribute( "温度2" ).Value; label3.Text = element.Attribute( "温度3" ).Value; } } ) ); }
此处修改了一点,在显示数据之前,对topic进行了判断,如果是A的话,再进行显示,其他的不管。因为,我们只使用了一个MqttClient,所以在实例化form2的时候,就需要form1的client传递给form2。
然后form2窗体初始化的时候,就需要订阅B,然后窗体关闭的时候,要取消订阅B,然后绑定的事件进行解绑操作。当然了,显示主题B的数据的时候,也是需要对B主题进行判断的。代码如下:
using HslCommunication.MQTT; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApp1 { public partial class Form2 : Form { public Form2( MqttClient client ) { InitializeComponent( ); this.client = client; } private void Form2_Load( object sender, EventArgs e ) { // 接收到数据的时候进行触发 client.OnMqttMessageReceived += Client_OnMqttMessageReceived; // 订阅服务器的主题,在连接成功后就去订阅 client.SubscribeMessage( "B" ); } private void Client_OnMqttMessageReceived( string topic, byte[] payload ) { // 切回主线程进行显示文本 Invoke( new Action( ( ) => { if (topic == "B") { label1.Text = Encoding.UTF8.GetString( payload ); } } ) ); } private MqttClient client; private void Form2_FormClosing( object sender, FormClosingEventArgs e ) { client.UnSubscribeMessage( "B" ); client.OnMqttMessageReceived -= Client_OnMqttMessageReceived; } } }
关于MQTT服务器更多高级的功能,比如加入ID过滤,用户名和密码的验证,等等其他的内容,也可以下面的博文:
https://www.cnblogs.com/dathlin/p/12312952.html
可以自己实现一些非常高级的功能。此处就不再过多的赘述了。
客户端也可以输入id,用户名和密码,来实现一个高级的操作,还有日志部分的内容。客户端详细的说明参照下面:
https://www.cnblogs.com/dathlin/p/11631894.html
情景二:
我有一个提供服务的服务器程序,用来给其他的客户端提供服务器,客户端发送数据给服务器,服务器进行相关的处理,然后返回结果给客户端。强调的是一个问答机制,客户端问,服务器答。
在这种情况下,同时客户端也可以用来上传数据,下载数据,当然了,客户端在连接服务器的时候,最好能标记一个客户端的id信息,最好能输入用户名密码进行验证。
这一切hslcommunication都已经支持了。而且已经集成在了MQTT的服务器里面。我还是拿上述的项目作为例子来说明。我现在有一个需求,我在客户端里,有两个按钮,用来控制,服务器的MQTT的发布的实时数据的。
这时候,可能有人会问,mqtt客户端发布个数据,mqtt服务器进行拦截,判断就可以实现,是的,确实可以,不过无论是服务器,还是客户端,编程都是麻烦很多,而且客户端需要反馈回结果,是否操作成功,
下面就来说说很方便的实现。MQTT服务器,可以接收同步网络的请求,需要判断下session的协议类型即可。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using HslCommunication; using HslCommunication.MQTT; using Newtonsoft.Json.Linq; using System.Xml.Linq; namespace Server { class Program { static void Main( string[] args ) { MqttServer server = new MqttServer( ); server.ServerStart( 1883 ); bool isPublish = true; // 是否发布数据的标记。 server.OnClientApplicationMessageReceive += ( MqttSession session, MqttClientApplicationMessage message ) => { if(session.Protocol == "HUSL") { // 对同步网络进行处理 if(message.Topic == "STOP") { isPublish = false; // 停止发布数据 server.PublishTopicPayload( session, "SUCCESS", null ); // 返回操作成功的说明 } else if(message.Topic == "CONTINUE") { isPublish = true; // 继续发布数据 server.PublishTopicPayload( session, "SUCCESS", null ); // 返回操作成功的说明 } else { server.PublishTopicPayload( session, message.Topic, message.Payload ); // 其他的命令不处理,把原数据返回去 } } }; int i = 0; while(true) { System.Threading.Thread.Sleep( 1000 ); if (isPublish) { XElement element = new XElement( "Data" ); element.SetAttributeValue( "温度1", i ); element.SetAttributeValue( "温度2", i + 1 ); element.SetAttributeValue( "温度3", i + 2 ); server.PublishTopicPayload( "A", Encoding.UTF8.GetBytes( element.ToString( ) ) ); server.PublishTopicPayload( "B", Encoding.UTF8.GetBytes( i.ToString( ) ) ); i++; } } } } }
我们使用 isPublish 来控制数据的发布,这个bool的属性由客户端的按钮来决定的,那么客户端的两个按钮怎么写呢
我们用这两个按钮来控制服务器的bool变量。代码比较简单了。
private void button2_Click( object sender, EventArgs e ) { // 停止发布 MqttSyncClient syncClient = new MqttSyncClient( "127.0.0.1", 1883 ); OperateResult<string, string> read = syncClient.ReadString( "STOP", null ); if (read.IsSuccess) { if(read.Content1 == "SUCCESS") { MessageBox.Show( "通知关闭成功!" ); } else { MessageBox.Show( "通知关闭失败:" + read.Content1 ); } } else { MessageBox.Show( "通知关闭失败:" + read.Message ); } } private void button3_Click( object sender, EventArgs e ) { // 继续发布 MqttSyncClient syncClient = new MqttSyncClient( "127.0.0.1", 1883 ); OperateResult<string, string> read = syncClient.ReadString( "CONTINUE", null ); if (read.IsSuccess) { if (read.Content1 == "SUCCESS") { MessageBox.Show( "通知关闭成功!" ); } else { MessageBox.Show( "通知关闭失败:" + read.Content1 ); } } else { MessageBox.Show( "通知关闭失败:" + read.Message ); } }
我们此处的代码进行了一些验证,当接收到字符串为 SUCCESS 时才代表真的成功!我们来验证一下效果。
点击停止发布之后,服务器就立即停止了发布,点击继续发布,服务器就继续发布了。演示效果非常的满意。
可能此处有的小伙伴有疑问了,客户端为啥要判断服务器返回的是否是SUCCESS字符串呢?服务器只要正常反回,不都是这个字符串吗?
一般来说是的,但是此处有个更高级的用法,我们来假设这个场景,这两个按钮的权限等级很高,只有指定的账户才能操作,其他账户不能操作。比如账户名为 "hsl" 的账户
我们就可以来修改服务器的代码:
此时,我们再启动客户端来看看效果。
我们不仅返回了失败的结果,还把失败的原因也返回,方便客户端查看。此处可能会有老铁说,我客户端的软件,如果账户不是hsl,就把按钮给禁了,这也是个思路,不过实际大多数情况,都认为客户端不安全的,服务器都是需要进行安全检查的。
现在我们的客户端程序修改一下,设置一个用户名及密码之后。
这时候,就有权限进行操作了。如果你需要在服务器端,进行校验用户名和密码,那就修改服务器的验证连接,具体可以参考服务器端的博客文章。
https://www.cnblogs.com/dathlin/p/12312952.html
这时候,有小伙伴可能会有疑问,你这个同步访问的客户端,我好像没有看到连接的操作啊,实例化之后,就直接读写了啊。
上面的代码机制确实是这样的,原理是短连接,当你读取的时候,进行数据连接,当你读完了,就断开连接。如果我想长连接,确实这个客户端对象,会在很多不同的窗体用到,我只能实例化一个就够了。那么就是这么写代码。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.Xml.Linq; using HslCommunication; using HslCommunication.MQTT; using Newtonsoft.Json.Linq; namespace WindowsFormsApp1 { public partial class Form1 : Form { public Form1( ) { InitializeComponent( ); } private void Form1_Load( object sender, EventArgs e ) { client = new MqttClient( new MqttConnectionOptions( ) { IpAddress = "127.0.0.1", Port = 1883, } ); // 接收到数据的时候进行触发 client.OnMqttMessageReceived += Client_OnMqttMessageReceived; // 订阅服务器的主题,在连接成功后就去订阅 client.OnClientConnected += m => { m.SubscribeMessage( "A" ); }; client.ConnectServer( ); // 同步网络的实例化 syncClient = new MqttSyncClient( new MqttConnectionOptions( ) { IpAddress = "127.0.0.1", Port = 1883, Credentials = new MqttCredential( "hsl", "123456" ) } ); syncClient.SetPersistentConnection( ); // 设置为长连接 } private void Client_OnMqttMessageReceived( string topic, byte[] payload ) { // 切回主线程进行显示文本 Invoke( new Action( ( ) => { // 我的form1只对A进行了订阅,按照道理应该只对A进行响应操作。 if (topic == "A") { XElement element = XElement.Parse( Encoding.UTF8.GetString( payload ) ); label1.Text = element.Attribute( "温度1" ).Value; label2.Text = element.Attribute( "温度2" ).Value; label3.Text = element.Attribute( "温度3" ).Value; } } ) ); } private MqttClient client; private void button1_Click( object sender, EventArgs e ) { using(Form2 form = new Form2( client )) { form.ShowDialog( ); } } private MqttSyncClient syncClient; private void button2_Click( object sender, EventArgs e ) { // 停止发布 OperateResult<string, string> read = syncClient.ReadString( "STOP", null ); if (read.IsSuccess) { if(read.Content1 == "SUCCESS") { MessageBox.Show( "通知关闭成功!" ); } else { MessageBox.Show( "通知关闭失败:" + read.Content1 ); } } else { MessageBox.Show( "通知关闭失败:" + read.Message ); } } private void button3_Click( object sender, EventArgs e ) { // 继续发布 OperateResult<string, string> read = syncClient.ReadString( "CONTINUE", null ); if (read.IsSuccess) { if (read.Content1 == "SUCCESS") { MessageBox.Show( "通知关闭成功!" ); } else { MessageBox.Show( "通知关闭失败:" + read.Content1 ); } } else { MessageBox.Show( "通知关闭失败:" + read.Message ); } } } }
可以看到,我们只实例化一个客户端的对象,syncClient,实例化之后,设置长连接即可。如果是多个窗体也需要使用syncClient对象的话,把这个对象syncClient传递给其他的窗体即可,用法和form1的代码是一样的。
using HslCommunication; using HslCommunication.MQTT; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApp1 { public partial class Form2 : Form { public Form2( MqttClient client , MqttSyncClient syncClient ) { InitializeComponent( ); this.client = client; this.syncClient = syncClient; } private void Form2_Load( object sender, EventArgs e ) { // 接收到数据的时候进行触发 client.OnMqttMessageReceived += Client_OnMqttMessageReceived; // 订阅服务器的主题,在连接成功后就去订阅 client.SubscribeMessage( "B" ); } private void Client_OnMqttMessageReceived( string topic, byte[] payload ) { // 切回主线程进行显示文本 Invoke( new Action( ( ) => { if (topic == "B") { label1.Text = Encoding.UTF8.GetString( payload ); } } ) ); } private MqttClient client; private MqttSyncClient syncClient; private void Form2_FormClosing( object sender, FormClosingEventArgs e ) { client.UnSubscribeMessage( "B" ); client.OnMqttMessageReceived -= Client_OnMqttMessageReceived; } private void button2_Click( object sender, EventArgs e ) { // 停止发布 OperateResult<string, string> read = syncClient.ReadString( "STOP", null ); if (read.IsSuccess) { if (read.Content1 == "SUCCESS") { MessageBox.Show( "通知关闭成功!" ); } else { MessageBox.Show( "通知关闭失败:" + read.Content1 ); } } else { MessageBox.Show( "通知关闭失败:" + read.Message ); } } private void button3_Click( object sender, EventArgs e ) { // 继续发布 OperateResult<string, string> read = syncClient.ReadString( "CONTINUE", null ); if (read.IsSuccess) { if (read.Content1 == "SUCCESS") { MessageBox.Show( "通知关闭成功!" ); } else { MessageBox.Show( "通知关闭失败:" + read.Content1 ); } } else { MessageBox.Show( "通知关闭失败:" + read.Message ); } } } }
这么写代码之后,我们的form2也具有通知关闭的功能了,在form1里面,我们只需要把客户端的对象传递给form2即可。
通过总结上面的代码,我们再仔细的看看服务器代码的核心部分,
这个Topic的功能类似一个命令码,服务器根据topic的信息来处理不同的数据,实现不同的功能,可能有的网友会问了,上述的命令交互我会了,现在我想通过这个来实现文件的上传下载,能不能实现?
答案也是可以的,我举例实现文件的下载功能,这个功能比较常见,我客户端可能需要向服务器实现请求下载一个文件。比如下载一个word文档。
此处在服务器的当前DEBUG目录,我放置了一个word文档。
我先写服务器侧的代码,这时候,我需要继续新增一个topic判断了,我们设定下载文件的命令吗是 DownloadFile
这部分的代码,就是把当前debug目录下的word文档发送给对方,当然你也可以指定绝对路径,此处为了灵活,因为服务器的程序不一定部署在我的电脑上的,还可能是别的电脑的。
客户端我们新增一个按钮,下载文件,同样也是保存到客户端当前的目录,然后通知word软件打开它。
点击按钮的代码如下:
此处就是保存在客户端的当前目录,我们来运行一下;
发现文件直接被打开了,我们来看看客户端的目录发生了什么变化?
确实下载了一个文件。我们再来想想,服务器再读取文件的时候,假设文件被占用了怎么办?读取失败了怎么办?文件不存在怎么办?难道服务器奔溃吗?当然不是了,我们依然可以进返回的Topic进行处理。
我们返回一个带操作是否成功的字符串回去,那么就可以用hsl自带的类。看代码演示。
我们此处就选择,一旦读取失败,就返回错误的界面给客户端。客户端解析部分的代码也需要稍微改一下
然后我们在跑一下服务器,客户端,发现没有问题,可以下载文件,并且打开这个文件。
这时候,我们尝试一下意外的情况,我们删除服务器的debug目录的文件,看看异常的情况会怎么显示。
好了,我们已经删除了,看看客户端,再点击下载的时候,会出现什么情况。
这时候的客户端可以友好的提醒错误的内容,文件不存在,还是被占用等等。此处还有一个问题需要思考,如果我下载的文件达到了10M大,我的服务器部署在局域网的另一台电脑上的,导致我客户端下载文件的时候,需要下载10秒钟,上面的代码会导致界面假死10分钟,为了不卡UI界面,我们需要使用后台线程来下载,这里举例async....await技术现实的后台下载。
只需要改两个地方,就可以实现后台的异步下载了。如果你的客户端是.net35的,就只能乖乖使用thread类来实现后台线程了。
这时候,可能又有小伙伴说了,我一个文件要下载10秒钟,可能1分钟,可能更长,那我现在下载的进度也想知道,不然用户就不知道现在还要等待多久,体验不好。
没事的,满足你。我们再修改下客户端的代码即可以实现下载进度。
我们再添加一个进度条控件,用来显示下载文件的进度的,我们在下载文件的时候进行更新进度的操作。
我们在下载的方法里传入一个下载进度的委托即可,同理,上传的进度也是一样的。可以仔细查看下
syncClient.ReadAsync 方法的参数的含义,和相关的用途。
这部分的内容最后有个细节,客户端接收数据都是接收到byte[]数组里的,实际上就是内存。如果你的文件很大,一个G,甚至更大,对客户端来说,很占内存,而且服务器管理多文件也不容易实现,如果你需要对文件进行管理,可以参考另一篇博客:
hsl中有专门的文件服务器,来处理高并发,大文件的上传下载的。还支持文件的一些信息管理。
https://www.cnblogs.com/dathlin/p/7746113.html
其中,通过网络的请求,支持java的版本,和python的版本,就是说,java和python都可以对C#的服务器进行数据请求。
情景三:
我有一个提供服务的服务器程序,用来给其他的网页,语言,或是前端的界面。或是我的系统需要对外提供服务。
那么使用webapi是一个很常见的方式,但是通常会怎么去创建一个webapi呢?新建一个asp.net mvc吗?或是grpc?
如果是复杂的webapi,用框架来创建,没有什么问题,但是我就想创建一个简单的webapi,提供几个接口就行,最好直接集成到我的winform,wpf或是控制台程序,不需要再多开软件,比较麻烦。
这样也是可以实现的。
这样就可以启动一个服务器。我们使用postman来测试
同理,GETB也是一样的,此处就不再赘述了,详细的博客,请参照下面的地址:
https://www.cnblogs.com/dathlin/p/12335933.html
情景四,我想把数据推送给网页实时更新。
参考下面的博客。
https://www.cnblogs.com/dathlin/p/12303098.html