.NET 8中Android APP断连时HttpContext.RequestAborted未触发取消
问题分析与解决方案
可能原因
- TCP连接检测延迟:移动端关闭WiFi后,TCP连接不会立即发送终止信号(FIN/RST),Kestrel依赖TCP层的超时机制检测死连接,默认超时时间较长(比如默认IdleTimeout为2分钟),导致
RequestAborted令牌无法立即触发。而本地Postman测试时,同一机器内的TCP连接断开会被快速检测到,因此令牌正常工作。 - Kestrel默认配置不适配移动端场景:默认的连接超时参数未针对移动端不稳定网络优化,无法及时识别连接丢失。
- 移动端上传未主动取消请求:安卓APP在网络断开后,未主动终止上传请求,导致服务器端仍在等待数据,未触发取消逻辑。
解决方案
1. 调整Kestrel连接超时配置
在Program.cs中修改Kestrel的IdleTimeout和KeepAliveTimeout,缩短连接检测时间:
builder.WebHost.ConfigureKestrel(options => { options.Limits.IdleTimeout = TimeSpan.FromSeconds(30); options.Limits.KeepAliveTimeout = TimeSpan.FromSeconds(30); });
这样服务器会更快检测到长时间无数据传输的死连接,触发RequestAborted。
2. 合并超时令牌与RequestAborted
手动创建超时令牌,和RequestAborted合并,确保即使TCP超时未触发,也能在指定时间内取消操作:
using var cts = CancellationTokenSource.CreateLinkedTokenSource(HttpContext.RequestAborted); cts.CancelAfter(TimeSpan.FromSeconds(60)); // 设置60秒超时 await using var fileStream = IOFile.Create(filePath, bufferSize: 1024); try { await fileSection.FileStream?.CopyToAsync(fileStream, cts.Token); } catch (OperationCanceledException) { // 处理取消逻辑,比如删除未完成的文件 if (IOFile.Exists(filePath)) { IOFile.Delete(filePath); } throw; }
3. 监听流读取状态辅助判断
在流式上传过程中,额外检查流的读取返回值,如果连续多次读取到0字节,主动触发取消:
await using var fileStream = IOFile.Create(filePath, bufferSize: 1024); var buffer = new byte[1024]; int bytesRead; var emptyReadCount = 0; while ((bytesRead = await fileSection.FileStream.ReadAsync(buffer, 0, buffer.Length, HttpContext.RequestAborted)) > 0) { await fileStream.WriteAsync(buffer, 0, bytesRead, HttpContext.RequestAborted); emptyReadCount = 0; } // 连续多次空读,判定连接丢失 if (emptyReadCount >= 3) { HttpContext.RequestAborted.ThrowIfCancellationRequested(); throw new OperationCanceledException("Connection lost due to empty reads"); }
4. 移动端主动取消上传
在安卓APP中监听网络状态变化,当检测到WiFi断开时,主动取消HttpClient的上传请求:
// 安卓端示例(C# .NET for Android) var httpClient = new HttpClient(); var cts = new CancellationTokenSource(); // 监听网络状态,断开时取消 Connectivity.ConnectivityChanged += (s, e) => { if (!e.NetworkAccess.HasFlag(NetworkAccess.Internet)) { cts.Cancel(); } }; // 执行上传 try { var content = new StreamContent(fileStream); await httpClient.PostAsync(uploadUrl, content, cts.Token); } catch (OperationCanceledException) { // 处理取消逻辑 }
内容的提问来源于stack exchange,提问作者Iron
相关产品推荐
相关产品推荐

