问题
在使用 HttpWebRequest 时,同时使用了 5 个线程。
开始时都是使用本地搭建的临时服务器,后来换成了外网服务器,但是下载的文件都较小,在网络条件好的时候很快就会下载完成。
所以大多数情况下都未曾出现问题。
测试代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
using UnityEngine;
using System.Net;
using System.Threading;
public class TestConnectionLimit : MonoBehaviour
{
void Download(object arg)
{
var sw = new System.Diagnostics.Stopwatch();
sw.Start();
string url = arg as string;
try
{
var request = WebRequest.Create(url);
request.GetResponse();
}
catch (System.Exception e)
{
Debug.LogWarning(e.ToString());
}
finally
{
Debug.Log(url + " elapsedTimeInMilliseconds: " + sw.ElapsedMilliseconds);
}
}
void DownloadAsync(string url)
{
var thread = new Thread(new ParameterizedThreadStart(Download));
thread.Start(url);
}
void Start()
{
DownloadAsync("http://mat1.gtimg.com/www/images/qq2012/qqlogo_1x.png");
DownloadAsync("http://mat1.gtimg.com/www/images/qq2012/qqlogo_1x.png");
DownloadAsync("http://mat1.gtimg.com/www/images/qq2012/qqlogo_1x.png");
DownloadAsync("http://mat1.gtimg.com/www/images/qq2012/qqlogo_1x.png");
DownloadAsync("http://mat1.gtimg.com/www/images/qq2012/qqlogo_1x.png");
}
}
|
测试代码地址:
C# API WebRequest default connection limit is 2. Use the request.ServicePoint.ConnectionLimit to unlock this limitation
前段时间加入了下载速度限制功能,同时增加了监视调试下载进度界面。
然后发现在限速的情况下:
- 同一时间只会有两个文件在下载
- 其他全都是在挂起状态,且过 100 秒后会抛出超时异常
异常信息如下:
1
2
3
4
5
6
|
System.Net.WebException: The request timed out
at System.Net.HttpWebRequest.EndGetResponse (IAsyncResult asyncResult) [0x00000] in <filename unknown>:0
at System.Net.HttpWebRequest.GetResponse () [0x00000] in <filename unknown>:0
at TestConnectionLimit.Download (System.Object arg) [0x0001d] in Assets/TestConnectionLimit.cs:18
UnityEngine.Debug:LogWarning(Object)
TestConnectionLimit:Download(Object) (at Assets/TestConnectionLimit.cs:22)
|
注意:100 秒超时时间是默认时间,可以修改。
环境
- Unity 5.4.2f2
- Mac OS X 10.11.4
查找原因
因为使用了 Unity,也就是使用了 Mono,而不是 .Net,所以要搜索的范围大了很多。最终,在一篇博客上 System.Net.WebException when issuing more than two concurrent WebRequest’s – Wade Wegner 找到完全相同的问题,由此确定了问题的原因:HTTP 1.1 中规定了此限制,即连接同一主机的并发连接数不可以超过 2 个。
“Clients that use persistent connections SHOULD limit the number of simultaneous connections that they maintain to a given server. A single-user client SHOULD NOT maintain more than 2 connections with any server or proxy. A proxy SHOULD use up to 2*N connections to another server or proxy, where N is the number of simultaneously active users. These guidelines are intended to improve HTTP response times and avoid congestion.”
HTTP/1.1: Connections - Section 8.1.4
分析:由于有以上 2 个并发连接数限制,其他开启的连接会直接等待,直到超时。
解决方法
解决方法最直接的就是提升连接到同一服务器的并发连接数量。
上面博客中提到的提升并发连接数量方法是针对 .NET 应用的,在 Unity 中并无这样的配置文件。同时,我们需要的是动态修改此数量,方便与程序结合动态控制同时下载数量。
在 StackOverflow 上找到了解决方法 .net - How can I programmatically remove the 2 connection limit in WebClient - Stack Overflow
最佳答案的核心代码如下:
1
2
|
var request = WebRequest.Create(url) as HttpWebRequest;
request.ServicePoint.ConnectionLimit = 10;
|
必须在创建后动态设置,经测试有效。而全局静态的 System.Net.ServicePointManager.DefaultConnectionLimit = 10
经测试无效。
将上述改动加入到前面的测试代码中,再次运行后发现可以同时建立了 5 个并发请求并成功完成了。