[C#]平行處理網路傳輸時因連線數不足發生連線Timeout的解決方案
最近專案程式發生了一個很奇妙的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<WaitHandle> finished = new List<WaitHandle>();
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拿掉,程式就可以把所有的網路傳輸正常的跑完。