当前位置: 首页 > news >正文

C# 客户端服务器开发 异步实现

  利用TcpListener和TcpClient类在同步方式下监听客户端连接、接受、发送数据时,在操作没有完成之前,一直处于阻塞状态,这对于接收、发送数据量不大的情况下,或者操作用时比较短的情况下是比较方便的。但对于执行完成时间较长的任务,例如传送大文件等,最好使用异步操作。

  异步操作的最大优点是可以在一个操作没有完成之前同时进行其他的操作。.NET框架提供了一种成为AsyncCallBack(异步回调)的委托,该委托允许启动异步的功能,并在条件具备时,调用提供的回调方法(一种在操作或活动完成时,由委托自动调用的方法),然后在这个方法中完成并结束未完成的工作。

 

1、AsyncCallback委托

  AsyncCallback委托用于引用异步操作完成时调用的回调方法。在异步操作方式下,由于程序可以在启动异步操作后继续执行其他代码,因此必须有一种机制,以保证该异步操作完成时能及时通知调用者。这种机制可以通过AsyncCakkback委托实现。

  异步操作的每一个方法都有一个Begin...函数和一个End...函数。当程序调用Begin...方法时,系统会自动在线程池中创建对应的线程进行异步操作,从而保证调用方法和被调用方法同时执行,当线程池中的Begin...方法执行完毕时,会自动通过AsyncCallback委托调用Begin...函数的参数中指定的回调函数。

  回调函数是在程序中事先定义好的,在回调函数中,通过End...函数获取Begin...函数的返回值和所有输入/输出参数,从而达到异步操作方式下完成参数传递的目的。

 

2、BeginAcceptTcpClient函数和EndAcceptTcpClient函数

  在异步Tcp应用变成中,服务端可以使用TcpListener提供的BeginAcceptTcpClient函数开始接受新的额客户端连接请求。在这个方法中,系统自动利用线程池创建需要的线程,并在操作完成时利用异步回调机制调用提供给他的方法,同时返回相应的状态参数。例:

AsyncCallback callback = new AsyncCallback(AcceptTcpClientCallback);
tcpListener.BeginAcceptTcpClient(callback,tcpListener);

  上面的例子,当程序执行BeginAcceptTcpClient函数后,立即在线程池中创建需要的线程,同时在自动创建的线程中监听客户端连接请求。一旦接受了客户端连接请求,就自动通过委托调用提供给委托的方法AcceptTcpClientCallback函数,并通过tcpListener这个实例对象来返回状态信息。AcceptTcpClientCallback函数是自己定义的,定义格式为:

void AcceptTcpClientCallback(IAsyncResult ar)
{
        //HFY::回调函数中执行的代码
        //...
        TcpListener myListener = (TcpListener)ar.AsyncState;
        TcpClient client = myListener.EndAcceptTcpClient(ar);
       //...
}

  上面自己定义的回调函数中传递的参数必须是IAsyncResult类型的接口,它表示异步操作的状态。通过委托,系统会自动将该异步操作的状态信息从关联的BeginAcceptTcpClient函数传递到自定义的AcceptTcpClientCallback函数。

  此外,注意:在回调代码中,必须调用EndAcceptTcpClient函数,完成客户端连接。程序执行完EndAcceptTcpClient函数后,会自动完成客户端的连接请求,并返回包含底层套接字的TcpClient对象,之后就可以利用这个对象与客户端通信了。

  在默认情况下,程序执行BeginAcceptTcpClient函数后,在该函数返回状态信息之前,不会想同步TCP方式那样阻塞等待客户端连接,而是继续往下执行。如果我们希望在其返回状态信息之前阻塞当前线程的执行,可以调用ManualResetEvent对象的WaitOne函数。

 

 3、BeginConnect函数和EndConnect函数

   在异步TCP应用变成中,BeginConnect函数通过异步的方式向远程主机发出连接请求。BeginConnect函数在操作完成前不会阻塞,在程序中调用BeginConnect函数时,系统会自动用独立的线程来执行该方法,直到与远程主机连接成功或抛出一场nag。如果在调用BeginConnect函数之后想阻塞当前线程,也可以调用ManualResetEvent对象的WaitOne函数。

  异步BeginConnect函数只有在调用了EndConnect函数之后才算执行完毕,因此程序中需要在提供给委托调用的方法中调用TcpClient对象的EndConnect函数。关键代码为:

AsyncCallback requestCallback = new AsyncCallback(RequestCallBack);
tcpClient.BeginConnect("10.204.64.77",8088,requestCallback,tcpClient);

//HFY::回调函数
void RequestCallBack(IAsyncResult ar)
{
        //...
        tcpClient = (TcpClient)ar.AsyncState;
        //...
        tcpClient.EndConnect(ar);
        //...
}


4、发送数据

  在异步TCP编程中,如果本机已经和远程主机建立连接,就可以使用BeginWrite函数发送数据。BeginWrite函数用于向一个已经成功连接的套接字异步发送数据。在程序中调用BeginWrite函数后,系统会自动在内部产生的单独执行的线程中发送数据。

  使用BeginWrite异步发送数据,程序必须创建实现AsyncCallback委托的回调函数,并将回调函数的名称传递给BeginWrite函数。在回调函数中,必须调用EndWrite函数,并在EndWrite后,系统会自动使用单独的线程来执行指定的回调函数,并在EndWrite上一直处于阻塞状态,直到NetworkStream对象发送请求的字节数或者引发异常。

 

5、接收数据

  与发送数据类似,如果本机已经和远程主机建立连接,就可以使用BeginRead函数发送数据。BeginRead方法启动从传入网路缓冲区中异步读取数据的操作。在调用BeginRead函数后,系统自动在单独的执行线程中接受数据。

  在程序中必须创建实现AsyncCallback委托的回调函数,并将其名称传递给BeginRead。一般情况下,我们希望在回调函数中获得所接收的数据,因此因创建小型的类或者结构来保存读取缓冲区,以及其他有用的信息。

  在回调函数中,必须调用EndRead函数来完成读取操作。系统在执行BeginRead时,将一直等待知道数据接收完毕或者遇到错误,从而得到可用的字节数,再自动使用一个单独的线程来执行指定的回调函数,并阻塞EndRead,直到所提供的NetworkStream对象将可用的数据读取完毕,或者达到指定读取的字节数。

转载于:https://www.cnblogs.com/HanEichy/p/4503481.html

相关文章:

  • 打印mac地址
  • (转)EXC_BREAKPOINT僵尸错误
  • phantomjs技巧之将html页面转换为pdf文件
  • 完毕port(CompletionPort)具体解释 - 手把手教你玩转网络编程系列之三
  • 入门到进阶:最全面Vim快捷键键位图
  • github帐号及文章选题
  • 【Datastage】NULL VALUE TO A NOT NULL VALUE
  • MongoDB-3.4安装文档
  • C#开发微信门户及应用(28)--微信“摇一摇·周边”功能的使用和接口的实现
  • bug给你带来的四个好处
  • C++中怎样获取类成员的指针
  • lol盒子重点内容
  • 安卓逆向系列教程 4.6 去广告
  • Myeclipse安装SVN插件
  • 王国维读书三大境界
  • 【剑指offer】让抽象问题具体化
  • Docker容器管理
  • Javascript弹出层-初探
  • Lucene解析 - 基本概念
  • Traffic-Sign Detection and Classification in the Wild 论文笔记
  • 程序员最讨厌的9句话,你可有补充?
  • 从@property说起(二)当我们写下@property (nonatomic, weak) id obj时,我们究竟写了什么...
  • 极限编程 (Extreme Programming) - 发布计划 (Release Planning)
  • 聊聊sentinel的DegradeSlot
  • 浅谈JavaScript的面向对象和它的封装、继承、多态
  • 如何设计一个微型分布式架构?
  • 实战|智能家居行业移动应用性能分析
  • 提醒我喝水chrome插件开发指南
  • HanLP分词命名实体提取详解
  • JavaScript 新语法详解:Class 的私有属性与私有方法 ...
  • Linux权限管理(week1_day5)--技术流ken
  • 长三角G60科创走廊智能驾驶产业联盟揭牌成立,近80家企业助力智能驾驶行业发展 ...
  • 大数据全解:定义、价值及挑战
  • 交换综合实验一
  • # Apache SeaTunnel 究竟是什么?
  • #define用法
  • #pragma 指令
  • #传输# #传输数据判断#
  • #我与Java虚拟机的故事#连载19:等我技术变强了,我会去看你的 ​
  • (C#)if (this == null)?你在逗我,this 怎么可能为 null!用 IL 编译和反编译看穿一切
  • (libusb) usb口自动刷新
  • (附源码)python房屋租赁管理系统 毕业设计 745613
  • (十)c52学习之旅-定时器实验
  • .\OBJ\test1.axf: Error: L6230W: Ignoring --entry command. Cannot find argumen 'Reset_Handler'
  • .360、.halo勒索病毒的最新威胁:如何恢复您的数据?
  • .bat批处理(七):PC端从手机内复制文件到本地
  • .NET 自定义中间件 判断是否存在 AllowAnonymousAttribute 特性 来判断是否需要身份验证
  • .NET面试题(二)
  • .NET学习全景图
  • .vue文件怎么使用_我在项目中是这样配置Vue的
  • @Repository 注解
  • @SuppressLint(NewApi)和@TargetApi()的区别
  • [AIGC] 使用Curl进行网络请求的常见用法
  • [C++]AVL树怎么转
  • [CC2642R1][VSCODE+Embedded IDE+IAR Build+Cortex-Debug] TI CC2642R1基于VsCode的开发环境