在 C# 中使用 httpclient 保持 TCP 端口打开
Posted
技术标签:
【中文标题】在 C# 中使用 httpclient 保持 TCP 端口打开【英文标题】:Keep TCP port open using httpclient in C# 【发布时间】:2014-12-29 19:31:28 【问题描述】:我是异步编程的新手,我正在尝试使用 httpclient 为页面内容触发批量 URL 请求。 这是我的尝试:
private async void ProcessUrlAsyncWithHttp(HttpClient httpClient, string purl)
Stopwatch sw = new Stopwatch();
sw.Start();
HttpResponseMessage response = null;
try
Interlocked.Increment(ref _activeRequestsCount);
var request = new HttpRequestMessage()
RequestUri = new Uri(purl),
Method = HttpMethod.Get,
;
request.Headers.TryAddWithoutValidation("User-Agent", "MozillaMozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/39.0.2171.95 Safari/537.36");
request.Headers.TryAddWithoutValidation("Accept", "text/html,*.*");
request.Headers.TryAddWithoutValidation("Connection", "Keep-Alive");
request.Headers.TryAddWithoutValidation("Accept-Encoding", "gzip, deflate, sdch");
request.Headers.TryAddWithoutValidation("Accept-Language", "en-US,en;q=0.8");
response = await httpClient.SendAsync(request).ConfigureAwait(false);
string html = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
response.Dispose();
if (IsCaptcha(html)) throw new Exception("Captcha was returned");
request.Dispose();
Interlocked.Increment(ref _successfulCalls);
catch (HttpRequestException hex)
Console.WriteLine("http:" + hex.Message);
Interlocked.Increment(ref _failedCalls);
catch (Exception ex)
Console.WriteLine(ex.GetType().AssemblyQualifiedName + " " + ex.Message);
Interlocked.Increment(ref _failedCalls);
finally
Interlocked.Decrement(ref _activeRequestsCount);
Interlocked.Decrement(ref _itemsLeft);
if (response != null) response.Dispose();
if (httpClient != null) httpClient.Dispose();
sw.Stop();
DateTime currentTime = DateTime.UtcNow;
TimeSpan elapsedTillNow = (currentTime - _overallStartTime).Duration();
Console.WriteLine("Left:" + _itemsLeft + ", Current execution:" + sw.ElapsedMilliseconds + " (ms), Average execution:" + Math.Round((elapsedTillNow.TotalMilliseconds / (_totalItems - _itemsLeft)), 0) + " (ms)");
lock(_syncLock)
if (_itemsLeft == 0)
_overallEndTime = DateTime.UtcNow;
this.DisplayTestResults();
如您所见,我将一个 httpclient 传递给该函数,并且每次下载 URL 时它都会被销毁。我知道这是一种矫枉过正的做法,理想情况下我们应该重用 httpclient。但是由于我不能为每个 URL 使用具有不同代理的单个 httpclient(处理程序需要传递给 httpclient 的构造函数并且不能更改,因此如果不重新创建 httpclient 对象就不能提供新的代理),我需要使用这个接近。
在调用方,我有一个非常基本的代码:
public async void TestAsyncWithHttp()
ServicePointManager.DefaultConnectionLimit = 10;
//ServicePointManager.UseNagleAlgorithm = false;
List<string> urlList = SetUpURLList();
urlList = urlList.GetRange(1, 50);
_itemsLeft = urlList.Count();
_totalItems = _itemsLeft;
List<string> proxies = new List<string>();
proxies.Add("124.161.94.8:80");
proxies.Add("183.207.228.8:80");
proxies.Add("202.29.97.5:3128");
proxies.Add("210.75.14.158:80");
proxies.Add("203.100.80.81:8080");
proxies.Add("218.207.172.236:80");
proxies.Add("218.59.144.120:81");
proxies.Add("218.59.144.95:80");
proxies.Add("218.28.35.234:8080");
proxies.Add("222.88.236.236:83");
Random rnd = new Random();
foreach (string url in urlList)
int ind = rnd.Next(0, proxies.Count-1);
var httpClientHandler = new HttpClientHandler
Proxy = new WebProxy(proxies.ElementAt(ind), false),
UseProxy = true
;
HttpClient httpClient = new HttpClient(httpClientHandler);
//HttpClient httpClient = new HttpClient();
httpClient.Timeout = TimeSpan.FromMinutes(2);
ProcessUrlAsyncWithHttp(httpClient, url);
问题是: 1)为什么每个请求都会关闭 TCP 端口。我想打开端口的最大连接数并在调用中重用它们。例如,在上面的示例中,我可以有 10 个并发连接。因此,我希望它打开 10 个 TCP 端口,然后其余 40 个请求可以串联使用这 10 个端口。这是 httpwebrequest 中预期的正常行为。我有一个使用 httpwebrequest 的工作代码,它描述了这种重用端口的行为。可以根据需要为任何想要查看的人发布该代码。因此,尽管它基于 httpwebrequest,但 httpclient 并没有模仿这种行为,这有点奇怪。
2) 对于此类调用,我们如何将 autoredirect 分配为 false? 3)我打算将此功能用于多个呼叫 - 说大约 50K。代码编写方式中可能需要更正的任何错误 4) 假设我以某种方式设法使用单个 httpclient 对象而不是每个请求一个对象。有什么方法可以确保我读取所有这些单独请求的 cookie 并在必要时更改它们,同时记住我有一个用于整个 URL 请求集的 httpclient 类?
Tks 卡洛尔
【问题讨论】:
连接重用不依赖于 HttpClient 重用。每次只需创建一个新客户。连接使用标准 .NET 池基础结构进行池化。 是的,我明白这一点。但是使用 httpwebrequest/response 编写了一个类似的代码段,第一次打开的 TCP 端口将用于以后的请求。所以问题是为什么使用类似的 httpclient 代码不会使用端口(当我们知道它在内部使用 httpwebrequest/response 时) 【参考方案1】:根据我的经验(我曾经遇到过类似的 TCP 端口拥塞问题,因为端口总是关闭,当我访问一个每分钟大约 6000 个连接的服务器时)重用 HttpClientHandler 对象就足够了,它实际上管理着连接池,并始终为每个请求重新创建 HttpClient 对象(使用带有 HttpClientManager 参数的构造函数)。
希望这会有所帮助。
马蒂亚斯
【讨论】:
【参考方案2】:您是否尝试过将 HttpClient 代码放入类中并创建 10 个类,每个类都有一个 HttpClient?
【讨论】:
以上是关于在 C# 中使用 httpclient 保持 TCP 端口打开的主要内容,如果未能解决你的问题,请参考以下文章
C# HttpClient使用和注意事项,.NET Framework连接池并发限制
如何在 C# 中使用 HttpClient 存储 cookie?