最近專案程式發生了一個很奇妙的BUG,專案程式在某些情況下網路傳輸會發生Timeout的現象,而且一發生就是一連串的網路傳輸都連帶Timeout。這問題很難重現,程式看起來邏輯都對,在大部分的情況下都看不到這種現象,開發團隊的電腦也沒有一台發生。後來查了一下網路文章,大膽推測是因為連線數過多造成的,可能是某些狀況下程式會同時有多個網路傳輸的連線,導致超過可容納的連線數造成等待而Timeout。

MSDN中的ServicePointManager.DefaultConnectionLimit 屬性這篇有提到,ServicePointManager.DefaultConnectionLimit在一般的WinForm程式中預設是2,而在ASP.NET中預設是10,而ServicePointManager.DefaultConnectionLimit這個屬性值又是HttpWebRequet.ServicePoint.ConnectionLimit的預設值,也就是說在WinForm下同時只能服務兩個連線數,超過兩個以上的連線自然就會被卡住而最後導致Timeout。

因此解決方法不外乎就是設定ServicePoint.ConnectionLimit的值,或是直接修改ServicePointManager.DefaultConnectionLimit,從預設值開始下手調整。這邊筆者附上完整的測試程式。

using System; using System.Collections.Generic; using System.Diagnostics; using System.Net; using System.Threading;

namespace ConsoleApplication15 { class Program { public static string url = “https://www.google.com/images/srpr/logo3w.png"; private static List finished = new List(); public static void Main() { ServicePointManager.DefaultConnectionLimit = 200; finished.Clear(); for (var idx = 0; idx < 5; ++idx ) { finished.Add(new ManualResetEvent(false)); Download(idx, url); } WaitHandle.WaitAll(finished.ToArray()); Console.WriteLine(“Done…”); }

private static void Download(int idx, string url) { var wc = new WebClient(); wc.OpenReadCompleted += wc_OpenReadCompleted; wc.OpenReadAsync(new Uri(url), idx); }

private static void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e) { Console.WriteLine(string.Format("{0}: Download Completed”, e.UserState));

var idx = (int)e.UserState; var manualResetEvent = finished[idx] as ManualResetEvent;

Debug.Assert(manualResetEvent != null, “manualResetEvent != null”); manualResetEvent.Set(); } } }

注意到裡面有一段ServicePointManager.DefaultConnectionLimit = 200;的程式,若是把它給Mark起來,我們可以發現運行起來程式會跑兩個網路傳輸就卡住了。

若是將Mark拿掉,程式就可以把所有的網路傳輸正常的跑完。

Link

WebClient并发下载问题

How can I programmatically remove the 2 connection limit in WebClient

ServicePointManager.DefaultConnectionLimit 屬性