我尝试使用await / async读取HttpWebResponse流:
async static Task testHttpWebClientAsync()
{
string url = "http://localhost/1.txt";
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
req.Method = "GET";
HttpWebResponse resp = (HttpWebResponse)await req.GetResponseAsync();
Stream stream = resp.GetResponseStream();
stream.ReadTimeout = 10 * 1000;
byte[] buffer = new byte[1024];
while (await stream.ReadAsync(buffer, 0, buffer.Length) > 0)
{
//time out exception never thrown
}
}
但是它不起作用,它永远不会在ReadAsync上超时。 为了进行比较,非异步版本可与同一台localhost测试服务器完美配合:
static void testHttpWebClient()
{
string url = "http://localhost/1.txt";
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
req.Method = "GET";
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
Stream stream = resp.GetResponseStream();
stream.ReadTimeout = 10 * 1000;
byte[] buffer = new byte[1024];
while (stream.Read(buffer, 0, buffer.Length) > 0)
{
//time out exception thrown here
}
}
以上代码已在控制台应用程序中经过测试:
static void Main(string[] args)
{
testHttpWebClient();
MainAsync(args).GetAwaiter().GetResult();
}
async static Task MainAsync(string[] args)
{
await testHttpWebClientAsync();
}
但这与问题无关,的确,我确实在WinForms项目中发现了问题,并创建了控制台项目来测试问题。
作为参考,测试服务器代码类似于:
int c = 10;
byte[] ba = new byte[1024];
SendHeader(sHttpVersion, sMimeType,(int) ba.Length*c, " 200 OK", ref mySocket);
for (int k = 0; k < c; k++)
{
//set break point here
SendToBrowser(ba, ref mySocket);
}
关于SO有几个类似的主题,但似乎没有一个解决这个问题。从API设计的角度来看,显然没有理由ReadAsync()不会像Read()那样超时,ReadAsync只需要监视套接字和内部计时器事件,这就是Task.Delay()的工作方式。这与CancellationToken等无关,因为我们甚至不需要取消任何内容,即使ReadAsync具有接受CancellationToken的版本。
所以这个问题既是问题的解决方案,又是为什么ReadAsync不仅超时而已。
答案 0 :(得分:1)
HttpWebRequest
上的异步API(以及WebClient
上的异步API,因为它内部使用HttpWebRequest
)在内部不使用超时。尽管我无法真正解释其背后的原因,但这是设计使然。
这尤其明显in the Write logic of the ConnectStream
(used internally by HttpWebResponse
):
if (async) {
m_Connection.BeginMultipleWrite(buffers, m_WriteCallbackDelegate, asyncResult);
}
else {
SafeSetSocketTimeout(SocketShutdown.Send);
m_Connection.MultipleWrite(buffers);
}
SafeSetSocketTimeout
是负责在基础套接字上设置超时的方法。如您所见,它是故意在异步路径上跳过的。读操作是同一回事,但是代码更复杂,因此更难以显示。
因此,您实际上只有两种解决方案:
.Abort()
上调用HttpWebRequest
的计时器)HttpClient