c# - 如何使用 NetworkStream.WriteAsync 取消等待?
问题描述
我正在玩,TcpClient
当我使用一些异步操作时,它们会忽略 CancellationToken。经过一番阅读,我知道这是故意的,也知道存在一些取消异步操作等待的方法。
我刚刚阅读了下一个 StackOverflow 问题和文章,其中阐明了一些要点:
https://devblogs.microsoft.com/pfxteam/how-do-i-cancel-non-cancelable-async-operations/
按照之前的文章,我可以取消NetworkStream.ReadAsync
,但是当我在NetworkStream.WriteAsync
.
我有这个代码作为最小的例子:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
CancellationTokenSource ctSource;
private async void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
string http_resp = "";
var request_uri = new Uri("http://icanhazip.com");
//BIG FILE as postdata to test the WriteAsync (use one that you have on your disk)
string contents = File.ReadAllText(@"C:\Portables\4test.txt");
string post_data = contents;
ctSource = new CancellationTokenSource();
CancellationToken ct = ctSource.Token;
Task<string> task = HttpRequestAsync(post_data, request_uri, ct);
try
{
http_resp = await task;
}
catch
{
http_resp = "General error";
}
textBox1.Text = http_resp;
button1.Enabled = true;
}
private static async Task<string> HttpRequestAsync(string post_data, Uri request_uri, CancellationToken ct)
{
string result = string.Empty;
string http_method = "POST";
string post_content_type = "application/x-www-form-urlencoded";
var hostname = request_uri.Host;
var port = request_uri.Port;
var scheme = request_uri.Scheme;
using (TcpClient tcpClient = new TcpClient())
{
tcpClient.SendTimeout = 15;
tcpClient.ReceiveTimeout = 15;
try
{
await tcpClient.ConnectAsync(hostname, port);
}
catch (Exception d1)
{
if (ct.IsCancellationRequested)
{
result = "Cancelation requested on ConnectAsync";
}
else
{
result = d1.Message + "\r\n" + d1.GetType().FullName + d1.StackTrace; ;
}
return result;
}
//Build HTTP headers
string reqString = "";
string header_host = "Host: " + hostname + "\r\n";
string header_close = "Connection: Close\r\n";
string basic_headers = "User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:71.0) Gecko/20100101 Firefox/71.0\r\n";
basic_headers += "Referer: https://www.google.com\r\n";
string header_post = "";
if (http_method == "POST")
{
string header_content_type = "";
header_content_type = "Content-type: " + post_content_type + "\r\n";
int content_length = 0;
content_length = post_data.Length;
string header_content_length = "Content-length: " + content_length + "\r\n";
header_post = header_content_type + header_content_length;
}
reqString = http_method + " " + request_uri.PathAndQuery + " " + "HTTP/1.1" + "\r\n" + header_host + basic_headers + header_close + header_post + "\r\n";
if (http_method == "POST")
{
reqString += post_data;
}
var header_bytes = Encoding.ASCII.GetBytes(reqString.ToString());
//Starting the I/O Network operations
using (NetworkStream tcp_stream = tcpClient.GetStream())
{
try
{
//HERE is where i have problems cancelling this await while WriteAsync is working.
await tcp_stream.WriteAsync(header_bytes, 0, header_bytes.Length, ct).WithCancellation(ct);
//await tcp_stream.WriteAsync(header_bytes, 0, header_bytes.Length, ct);
}
catch (Exception d2)
{
if (ct.IsCancellationRequested)
{
result = "Cancelation requested on WriteAsync";
}
else
{
result = d2.Message + "\r\n" + d2.GetType().FullName + d2.StackTrace;
}
return result;
}
using (var memory = new MemoryStream())
{
try
{
await tcp_stream.CopyToAsync(memory, 81920, ct);
}
catch (Exception d3)
{
if (ct.IsCancellationRequested)
{
result = "Request cancelled by user (on read)";
}
else
{
result = d3.Message + "\r\n" + d3.GetType().FullName + d3.StackTrace;
}
return result;
}
memory.Position = 0;
byte[] data = memory.ToArray();
result = Encoding.UTF8.GetString(data);
}
}
}
return result;
}
private void button2_Click(object sender, EventArgs e)
{
ctSource.Cancel();
}
}
当我在 ReadAsync 上使用它时效果很好:
await tcp_stream.ReadAsync(response, 0, response.Length, ct).WithCancellation(ct);
当我在 WriteAsync 上使用它时它不起作用:
await tcp_stream.WriteAsync(header_bytes, 0, header_bytes.Length, ct).WithCancellation(ct);
没有返回错误,只是没有取消等待。为了更清楚,我添加了一个最小示例作为 Visual Studio 2015 项目,您可以在此处下载:https ://github.com/Zeokat/minimal_ex/archive/master.zip
它还包括一个文件4test.rar
,您可以将其解压缩为 39MB 的文件4test.txt
。我使用这个文本文件作为post_data
测试的内容,因为在运行时调用取消操作足够大WriteAsync
。
有人可以帮我解决这个问题吗?我花了几天时间试图解决这个问题,但无法找到合适的解决方案。
提前致谢。
解决方案
不要使用 .WithCancellation(ct) 仅使用 await tcp_stream.WriteAsync(header_bytes, 0, header_bytes.Length, ct)。
cts = 新的 CancellationTokenSource();
通过 ct = cts.Token
在 cancel_event() 中: if(cts != null) cts.Cancel();
推荐阅读
- jmeter - 动态并行 HTTP 请求
- boto3 - botocore.exceptions.ClientError:发生错误(AccessDeniedException)
- node.js - mongoDB排序命令没有返回正确的值
- python - 使用 tkinter 显示和保存饼图
- python - 如何删除电报 api 中的队列更新?
- python - Python,用“with”语句包装函数的装饰器
- ios - 在 Cloud Kit 中获取 CKAsset 的大小
- ffmpeg - HW Accel 转码 Intel 快速同步视频 QSV h264_qsv 和 CRF 质量
- haskell - 为什么它是最外层的redex?
- haskell - 使用 ALE linter 处理 Haskell 文件时出现不正确的导入错误