英文:
Is there way to cancel or kill a task which waits for a response from a http client in c#?
问题
由于我在过去两天进行了谷歌搜索,仍然无法解决:
我正在使用Unity和C#。我有一个HTTP客户端,它会向我的本地服务器发送异步的GET请求。这个服务器被设置为在发送响应之前始终等待一定的时间。这是为了模拟一些实际服务器不会立即响应的行为。
举个例子,让我们假设我的服务器在发送响应之前等待20秒。我的客户端设置为在3秒内超时。
现在,我假设会在3秒后因超时而抛出错误并取消请求,但事实并非如此。错误会在客户端接收到响应后,也就是在20秒后抛出。
问题在于“真实”的服务器并不会回复每个请求。也许是因为我发送请求得太快了?无论如何,这些请求永远不会得到回复,而且会一直存在。
我的客户端代码如下(Unity中的C#):
private HttpClient _client = new HttpClient(); // 创建http客户端
public void Start()
{
_client.BaseAddress = new System.Uri("http://127.0.0.1:8000"); // 设置基本URL
_client.Timeout = new System.TimeSpan(0, 0, 0, 0, 3000); // 设置超时为3秒,也可以是500毫秒
GetRequest();
}
private async Task GetRequest()
{
try
{
var httpResponse = await _client.GetAsync("/somepath_doesnt_matter"); // 发送请求并等待响应
string result = await task.Content.ReadAsStringAsync(); // 读取字符串
Debug.Log("完成"); // 一切正常
}
catch (Exception e)
{
Debug.Log(e.Message); // 已取消 <--- 在20秒后打印,而不是3秒
}
}
附注:使用CancellationTokenSource,在将其设置为3秒超时并将令牌传递给GetAsync函数会导致相同的行为。
是否有一种方法可以在超时后“立即”抛出错误并取消任务?
编辑:这里是完整的文件链接:https://pastebin.com/WJQjmngt
使用一个CancellationTokenSource,将其设置为在3秒后超时,并将一个令牌传递给GetAsync函数,会导致相同的行为。
编辑2:似乎Unity以某种原因进行了不同的处理。我在VS控制台应用程序中编写了代码,它按预期取消了。然而,在Unity中,它会等待响应才会抛出错误。我猜我会去Unity论坛询问出了什么问题。如果有人感兴趣,这里是代码和结果:pastebin.com/Tk4KHNmh
英文:
since i googled for the last 2 days and still can't figure it out:
I'm working with Unity and c#. I have a http client which sends async get requests to my local server. This server is set to always wait a certain amount of time until it sends a response. This is to simulate some behavior where the real server just doesn't answer.
For this example, let's say my server waits 20 seconds before sending a response. My client is set to timeout in 3 seconds.
Now i assumed that an error would be thrown after 3 seconds because of the timeout and the request is canceled but that's not the case. The error is thrown after the client receives a response, which is after 20 seconds.
The problem is that the "real" server doesn't answer every request. Maybe because i send requests to fast? Anyway, this requests are never answered and live forever.
My code for the client is as follow (C# in Unity):
private HttpClient _client = new HttpClient(); // create the http client
public void Start()
{
_client.BaseAddress = new System.Uri("http://127.0.0.1:8000"); // set base url
_client.Timeout = new System.TimeSpan(0, 0, 0, 0, 3000); // set the timeout to 3 seconds, could be 500 ms
GetRequest();
}
private async Task GetRequest()
{
try
{
var httpResponse = await _client.GetAsync("/somepath_doesnt_matter"); // send request and await response
string result = await task.Content.ReadAsStringAsync(); // read the string
Debug.Log("Done"); // all is good
} catch (Exception e)
{
Debug.Log(e.Message); // canceled <--- prints after 20 seconds, not 3
}
}
ps: using a CancellationTokenSource, setting it to timeout after 3 seconds and passing a token to the GetAsync function results in the same behavior.
Is there a way to "immediately" throw the error after the timeout and cancel the task?
Edit: here ist he full file: https://pastebin.com/WJQjmngt
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
public class Example1 : MonoBehaviour
{
private HttpClient _client = new HttpClient();
void Start()
{
_client.BaseAddress = new System.Uri("http://127.0.0.1:8000");
_client.Timeout = new System.TimeSpan(0, 0, 0, 0, 3000);
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
GetRequest();
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
}
private async Task GetRequest()
{
try
{
CancellationTokenSource cts = new CancellationTokenSource();
cts.CancelAfter(3000);
var task = await _client.GetAsync("/somepath", cts.Token);
string result = await task.Content.ReadAsStringAsync();
Debug.Log("GOT:\n" + result);
}
catch (Exception e){
Debug.Log("EXCEPTION ============================");
Debug.Log(e.Message);
}
}
}
Edit2:
Seems like Unity do things different for whatever reason. I wrote the code in VS Console Application and it canceled as expected. In Unity however it waits for the response to throw the error. I guess i go to unity forums to ask whats wrong. Here the code and the results if someone is interested: pastebin.com/Tk4KHNmh
答案1
得分: 1
请注意,一旦您使用了您的httpclient,就不允许再更改它的超时时间。我怀疑您在设置超时值之前对其进行了某些操作。我还会改变您设置超时的方式,以使其更可读。
try
{
_client.Timeout = TimeSpan.FromSeconds(3);
HttpResponseMessage result = await client.GetAsync(uri);
string response = await result.Content.ReadAsStringAsync();
}
catch { /*timeout*/}
这段代码绝对有效(我从我的项目中取出了它),所以如果您遇到问题,那就是在设置超时之前使用了上下文。如果发生这种情况,它会被忽略。
您也可以通过诡计来强制触发超时异常。运行您自己的超时代码。下面的示例将在时间到期时手动引发超时异常。这应该在Unity中起作用,并强制调用退出。
await client.GetAsync()
.WithTimeout(TimeSpan.FromSeconds(3))
.ConfigureAwait(true);
以及事件:
public static async Task<TResult> WithTimeout<TResult>(this
Task<TResult> task, TimeSpan timeout)
{
if (task == await Task.WhenAny(task, Task.Delay(timeout)))
{
return await task;
}
throw new TimeoutException();
}
英文:
Be aware that once you've used your httpclient, you are no longer allowed to change the timeout on it. I suspect you used it for something before setting the timeout value. I would also change how you're setting your timeout so it's readable.
try
{
_client.Timeout = TimeSpan.FromSeconds(3);
HttpResponseMessage result = await client.GetAsync(uri);
string response = await result.Content.ReadAsStringAsync();
}
catch{ /*timeout*/}
This code absolutely works ( i took it from my own project) so if you're having problems, you're using the context before setting the timeout. It gets ignored if that happens.
You can also force a timeout exception by trickery. Run your own timeout code. The sample below will manually throw a timeout exception when the time expires. This should work in Unity and it forces the call to quit.
await client.GetAsync()
.WithTimeout(TimeSpan.FromSeconds(3))
.ConfigureAwait(true);
and the event:
public static async Task<TResult> WithTimeout<TResult>(this
Task<TResult> task, TimeSpan timeout)
{
if (task == await Task.WhenAny(task, Task.Delay(timeout)))
{
return await task;
}
throw new TimeoutException();
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论