一、背景

DotNetty 本身是一个优秀的网络通讯框架,不过它是基于异步事件驱动来处理另一端的响应,需要在单独的 Handler 去处理相应的返回结果。而在我们的实际使用当中,尤其是 客户端程序 基本都是 请求-响应 模型,在发送了数据时候需要等待服务器的响应才能进行下一步操作,如果服务器返回的是错误信息,则需要进行特殊的处理。

类似于下面这种方式:

public async void Button1_Click()
{
var result = await DotNettyClient.SendData("Hello"); if(result == "Error")
{
throw new Exception("服务器返回错误!");
} Console.WriteLine($"Hello {result}");
}

二、解决思路

参阅了大部分资料之后,发现在 Java 的 Netty 当中可以使用 Future / Promise 来实现,那么 C# 是否有类似的组件呢?答案是有的,他们对应的就是 TaskTaskCompletionSource,前者是给调用者的任务,而后者则是用于设置响应任务的结果。

那么我们就可以这么来处理,当客户端发送请求时,附带唯一的一个请求 ID,并将 TaskCompletionSource 放在一个请求字典当中,请求 ID 作为字典的 Key,值是 TaskCompletionSource,之后返回一个 Task。当客户端接收到服务器响应的时候,通过 TaskCompletionSource 设置之前那个 Task 的结果,这样我们接收到响应之后,就会从之前 await 的地方继续执行。

这里我自己的需求仅仅是类似于 同步阻塞式 的操作,所以我直接使用一个队列来做简单处理,并没有用唯一的请求 ID 来表示不同的请求,也没有使用字典,因为我可以 保证在同一时间内有且仅有一个客户端请求被发起,而且也做了响应的超时处理机制。

三、代码实现

实现起来超级简单,只需要在发起请求的时候,创建一个 TaskCompletionSource<TResponse> 对象。这个泛型参数指的是你想要的返回值类型,这里我以 TResponse 代替,下面的 DEMO 我会用 string 类型进行演示。

创建好一个 TaskCompletionSource<TResponse> 之后,在发送方法里面,我们可以将其对象放在一个先进先出的队列当中,然后将其 Task 属性作为发送方法的返回值。

我们再来到处理服务器响应的 Handler 当中,从队列里面拿去之前存放的 TaskCompletionSource<TResponse> 对象,调用其 SetResult() 方法,将具体响应进行设置。

通过以上的操作,我们在发送数据的时候,就可以使用 await 关键字等待服务端的响应,但不会阻塞线程,当客户端接收到服务端响应时,就会恢复到之前 await 的位置继续执行。

数据发送方法:

public static class DotNettyClient
{
static DotNettyClient()
{
RequestQueue = new Queue<TaskCompletionSource<string>>();
} public static Queue<TaskCompletionSource<string>> RequestQueue { get; set; } public static async Task<string> SendData(string data)
{
var resultTask = new TaskCompletionSource<string>(); var buffer = new Unpooled.Buffer();
buffer.WriteBytes(Encoding.UTF8.GetBytes(data));
await _clientChannel.WriteAndFlushAsync(buffer); RequestQueue.Enqueue(resultTask); return await resultTask.Task;
}
}

服务端响应处理:

public class ProtocolHandler : ChannelHandlerAdapter
{
public override void ChannelRead(IChannelHandlerContext context, object message)
{
if(message is string response)
{
if(!DotNettyClient.RequestQueue.TryDequeue(out TaskCompletionSource<string> result)) return;
result.SetResult(response);
}
}
}

这里我就不再编写解析器,主要说明一下代码的思路,下面在使用的时候就如同第一节说的一样,直接使用 await 关键字等待响应结果即可。

四、缺陷

在这里我并没有展示多个异步请求的情况,如果是用户同时发起多个请求的时候,你可以通过数据的唯一 ID 来标识每一个请求,读取时根据唯一 ID 从字典获取数据,这样在接收服务端响应的时候就能处理这种情况了。

五、参考资料

在 DotNetty 中实现同步请求的更多相关文章

  1. Http中的同步请求和异步请求

    最近在上springmvc的JSON数据交换的时候,老师下课提了一个课后问题:什么是异步请求?什么是同步请求?我想大部分同学听到这个问题的时候应该和我一样不知所云.现在,给大家分享一篇关于同步请求和异 ...

  2. 详细解读XMLHttpRequest(一)同步请求和异步请求

    本文主要参考:MDN XMLHttpRequest 让发送一个HTTP请求变得非常容易.你只需要简单的创建一个请求对象实例,打开一个URL,然后发送这个请求.当传输完毕后,结果的HTTP状态以及返回的 ...

  3. 【读书笔记】iOS网络-同步请求,队列式异步请求,异步请求的区别

    一,同步请求的最佳实践. 1,只在后台过程中使用同步请求,除非确定访问的是本地文件资源,否则请不要在主线程上使用. 2,只有在知道返回的数据不会超出应用的内存时才使用同步请求.记住,整个响应体都会位于 ...

  4. 【读书笔记】iOS-网络-同步请求,队列式异步请求,异步请求的区别

    一,同步请求的最佳实践. 1,只在后台过程中使用同步请求,除非确定访问的是本地文件资源,否则请不要在主线程上使用. 2,只有在知道返回的数据不会超出应用的内存时才使用同步请求.记住,整个响应体都会位于 ...

  5. JSON(五)——同步请求中使用JSON格式字符串进行交互(不太常见的用法)

    在同步请求中使用JSON格式进行数据交互的场景并不多,同步请求是浏览器直接与服务器进行数据交互的大多是用jsp的标签jstl和el表达式对请求中的数据进行数据的渲染.我也是在一次开发中要从其它服务器提 ...

  6. 第106天:Ajax中同步请求和异步请求

    同步请求和异步请求的区别 1.同步是指:发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式. 用户填写所有信息后,提交给服务器,等待服务器的回应(检验数据),是一次性的.信息错误又要重新 ...

  7. js ajax同步请求造成浏览器假死的问题

    一.问题的起因 今天做一个需求遇到了这么个情况,就是用户个人中心有个功能,点击按钮,可以刷新用户当前的积分,这个肯定需要使用到ajax的同步请求了,当时喀喀喀三下五除二写玩了,大概代码如下: /** ...

  8. 关于js中的同步和异步

    最近看到前端面试问到js中的同步和异步,这个问题该怎么回答? 梳理一下,js对于异步的处理,很多人的第一反应是ajax,这只能说是对了一半. 1.个人觉得,js中,最基础的异步是setTimeout和 ...

  9. IOS之同步请求、异步请求、GET请求、POST请求

    .同步请求可以从因特网请求数据,一旦发送同步请求,程序将停止用户交互,直至服务器返回数据完成,才可以进行下一步操作, .异步请求不会阻塞主线程,而会建立一个新的线程来操作,用户发出异步请求后,依然可以 ...

随机推荐

  1. 如何快速的开发一个完整的iOS直播app(美颜篇)

    前言 在看这篇之前,如果您还不了解直播原理,请查看这篇文章如何快速的开发一个完整的iOS直播app(原理篇) 开发一款直播app,美颜功能是很重要的,如果没有美颜功能,可能分分钟钟掉粉千万,本篇主要讲 ...

  2. linux 源码编译安装apache

    cc1 是c语言的编译器.

  3. Java之基于Eclipse搭建SSH框架(下)

    在上篇博客里,我简介了Tomcat滴配置与Struts2滴搭建,假设对这个还不会滴童鞋去看一下我滴上篇博客<Java之基于Eclipse搭建SSH框架(上)>.今天我们接着上篇博客滴内容. ...

  4. 使用word2010写文章发布到blog

    参考文档: http://www.cnblogs.com/liuxianan/archive/2013/04/13/3018732.html 使用Windows Live Writer 2012和Of ...

  5. Android--向SD卡读写数据

    // 向SD卡写入数据 private void writeSDcard(String str) { try { // 推断是否存在SD卡 if (Environment.getExternalSto ...

  6. HDU 1242 rescue (优先队列模板题)

    Rescue Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Subm ...

  7. 【学生信息管理系统】EOF 和 BOF

    敲完学生信息管理系统时,在删除信息的时候,常常会出现下图这种错误,遇到问题就要解决这个问题.经过查阅理解了记录集Recordset的EOF和BOF属性,用这两个属性能够知道记录集中是否有信息存在. E ...

  8. java-冒泡

    一.java冒泡排序 冒泡排序(Bubble Sort)是一种简单的排序算法.它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.走访数列的工作是重复地进行直到没有再需要 ...

  9. ViewGroup如何分发事件

    dispatchTouchEvent事件派发显示隧道方式.再是冒泡方式隧道方式传递,直道某一个元素消耗此事件,由上至下逐层分发视图.冒泡方式传递,当某个视图消耗事件后其return boolean 是 ...

  10. MongoDB安装和简单介绍

    前面我们把nodejs的web开发入门说了,如今来说说数据库,一般搭配的数据库是mysql和mongodb,今天我们来说mongodb MongoDB是一个基于分布式文件存储的数据库,由C++语言编写 ...