إذا كنت تطور تطبيقات .NET تحتاج إلى جلب بيانات من الويب، فستواجه حتمًا الحاجة إلى استخدام بروكسيات HTTP. سواء كان ذلك لتجاوز قيود المعدل Rate Limits، أو الوصول إلى محتوى محلي جغرافيًا، أو حماية هوية خوادمك، فإن إتقان التعامل مع البروكسيات في .NET 8+ أصبح مهارة أساسية للمطورين.
في هذا الدليل، سنغطي كل شيء من الأساسيات إلى الأنماط المتقدمة للإنتاج، مع أمثلة برمجية قابلة للتشغيل باستخدام C# HTTP proxy و .NET HttpClient proxy و C# residential proxies.
1. HttpClient مع HttpClientHandler و WebProxy: الأساسيات
الطريقة الأكثر شيوعًا لإعداد بروكسي في .NET هي استخدام HttpClientHandler مع WebProxy. هذا النهج مناسب لمعظم السيناريوهات البسيطة والمتوسطة.
إعداد بروكسي أساسي مع المصادقة
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
public class BasicProxyExample
{
private readonly HttpClient _httpClient;
public BasicProxyExample()
{
// إعداد البروكسي مع بيانات الاعتماد
var proxy = new WebProxy("http://gate.proxyhat.com:8080", bypassOnLocal: true)
{
Credentials = new NetworkCredential("user-country-US", "PASSWORD")
};
// قائمة التجاوز - المواقع التي لا تمر عبر البروكسي
proxy.BypassList = new string[]
{
"localhost",
"127.0.0.1",
"*.internal.local"
};
var handler = new HttpClientHandler
{
Proxy = proxy,
UseProxy = true,
UseDefaultCredentials = false,
// تعطيل التحقق من الشهادة للبروكسيات المحلية (لا تفعل هذا في الإنتاج)
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true
};
_httpClient = new HttpClient(handler)
{
Timeout = TimeSpan.FromSeconds(30)
};
}
public async Task<string> FetchAsync(string url)
{
try
{
var response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
catch (HttpRequestException ex)
{
Console.WriteLine($"Request failed: {ex.Message}");
throw;
}
}
}
استخدام بروكسي SOCKS5
للبروكسيات SOCKS5، تحتاج إلى مكتبة إضافية مثل System.Net.Http.WinHttpHandler على Windows أو استخدام SocketsHttpHandler مع دعم SOCKS:
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
public class Socks5ProxyExample
{
private readonly HttpClient _httpClient;
public Socks5ProxyExample()
{
// .NET 8+ يدعم SOCKS5 أصلاً عبر SocketsHttpHandler
var handler = new SocketsHttpHandler
{
Proxy = new WebProxy("socks5://gate.proxyhat.com:1080")
{
Credentials = new NetworkCredential("user-country-DE", "PASSWORD")
},
UseProxy = true
};
_httpClient = new HttpClient(handler);
}
public async Task<string> GetWithSocks5Async(string url)
{
return await _httpClient.GetStringAsync(url);
}
}
نصيحة مهمة: في .NET 8+، استخدم
SocketsHttpHandlerبدلاً منHttpClientHandlerللحصول على أداء أفضل ومزيد من التحكم.HttpClientHandlerهو غلاف حولSocketsHttpHandlerفي الإصدارات الحديثة.
2. SocketsHttpHandler: التحكم المتقدم لكل طلب
SocketsHttpHandler يوفر تحكمًا أدق في اتصالات البروكسي، بما في ذلك إمكانية تغيير البروكسي لكل طلب وإدارة دورة حياة الاتصالات.
إعداد SocketsHttpHandler مع PooledConnectionLifetime
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
public class AdvancedSocketsHandler
{
private readonly HttpClient _httpClient;
public AdvancedSocketsHandler()
{
var handler = new SocketsHttpHandler
{
Proxy = new WebProxy("http://gate.proxyhat.com:8080")
{
Credentials = new NetworkCredential("user-country-US", "PASSWORD")
},
UseProxy = true,
// إدارة تجمع الاتصالات
PooledConnectionLifetime = TimeSpan.FromMinutes(5),
PooledConnectionIdleTimeout = TimeSpan.FromMinutes(2),
MaxConnectionsPerServer = 100,
// مهلة الاتصال
ConnectTimeout = TimeSpan.FromSeconds(30),
// تفعيل HTTP/2
EnableMultipleHttp2Connections = true
};
_httpClient = new HttpClient(handler)
{
Timeout = TimeSpan.FromMinutes(5)
};
}
public async Task<HttpResponseMessage> SendWithCustomProxyAsync(
HttpRequestMessage request,
string proxyUrl,
string username,
string password)
{
// إنشاء handler جديد للطلب مع بروكسي مختلف
using var handler = new SocketsHttpHandler
{
Proxy = new WebProxy(proxyUrl)
{
Credentials = new NetworkCredential(username, password)
},
UseProxy = true,
ConnectTimeout = TimeSpan.FromSeconds(15)
};
using var client = new HttpClient(handler);
return await client.SendAsync(request);
}
// إعادة استخدام HttpClient مع بروكسي ديناميكي عبر Func
public HttpClient CreateDynamicProxyClient(Func<string> proxySelector)
{
var handler = new SocketsHttpHandler
{
Proxy = new CustomProxyWrapper(proxySelector),
UseProxy = true,
PooledConnectionLifetime = TimeSpan.FromMinutes(1)
};
return new HttpClient(handler);
}
}
// غلاف مخصص للبروكسي الديناميكي
public class CustomProxyWrapper : IWebProxy
{
private readonly Func<string> _proxySelector;
public CustomProxyWrapper(Func<string> proxySelector)
{
_proxySelector = proxySelector;
}
public Uri? GetProxy(Uri destination)
{
var proxyUrl = _proxySelector();
return string.IsNullOrEmpty(proxyUrl) ? null : new Uri(proxyUrl);
}
public bool IsBypassed(Uri host) => false;
public ICredentials? Credentials { get; set; }
}
لماذا PooledConnectionLifetime مهم؟
عند استخدام C# residential proxies دوارة، يجب تحديد PooledConnectionLifetime لضمان عدم إعادة استخدام اتصال قديم مع IP منتهي الصلاحية. القيمة المثالية تعتمد على سياسة دوران IP الخاصة بمزود البروكسي:
- جلسات لزجة Sticky Sessions: 5-10 دقائق
- دوران لكل طلب: 30 ثانية - دقيقة واحدة
- بروكسيات ثابتة: 30 دقيقة أو أكثر
3. Polly للمرونة: إعادة المحاولة و Circuit Breaker
Polly هي مكتبة أساسية لبناء تطبيقات HTTP مرنة. عند العمل مع البروكسيات، تحتاج إلى استراتيجية ذكية للتعامل مع الفشل.
using System;
using System.Net;
using System.Net.Http;
using Polly;
using Polly.Retry;
using Polly.CircuitBreaker;
public class ResilientHttpClient
{
private readonly HttpClient _httpClient;
private readonly AsyncRetryPolicy<HttpResponseMessage> _retryPolicy;
private readonly AsyncCircuitBreakerPolicy<HttpResponseMessage> _circuitBreaker;
public ResilientHttpClient()
{
var handler = new SocketsHttpHandler
{
Proxy = new WebProxy("http://gate.proxyhat.com:8080")
{
Credentials = new NetworkCredential("user-country-US", "PASSWORD")
},
UseProxy = true,
PooledConnectionLifetime = TimeSpan.FromMinutes(2)
};
_httpClient = new HttpClient(handler);
// سياسة إعادة المحاولة مع التراجع الأسي
_retryPolicy = Policy<HttpResponseMessage>
.Handle<HttpRequestException>()
.OrResult(r =>
r.StatusCode == HttpStatusCode.TooManyRequests ||
r.StatusCode == HttpStatusCode.RequestTimeout ||
r.StatusCode == HttpStatusCode.BadGateway ||
r.StatusCode == HttpStatusCode.ServiceUnavailable ||
r.StatusCode == HttpStatusCode.GatewayTimeout)
.WaitAndRetryAsync(
retryCount: 5,
sleepDurationProvider: retryAttempt =>
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
onRetry: (outcome, timeSpan, retryCount, context) =>
{
Console.WriteLine($"Retry {retryCount} after {timeSpan.TotalSeconds}s due to: {outcome.Exception?.Message ?? outcome.Result.StatusCode.ToString()}");
});
// سياسة Circuit Breaker
_circuitBreaker = Policy<HttpResponseMessage>
.Handle<HttpRequestException>()
.OrResult(r => (int)r.StatusCode >= 500)
.CircuitBreakerAsync(
exceptionsAllowedBeforeBreaking: 3,
durationOfBreak: TimeSpan.FromSeconds(30),
onBreak: (outcome, breakDelay) =>
{
Console.WriteLine($"Circuit broken for {breakDelay.TotalSeconds}s due to: {outcome.Exception?.Message ?? outcome.Result.StatusCode.ToString()}");
},
onReset: () =>
{
Console.WriteLine("Circuit reset");
});
}
public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)
{
// دمج السياسات
var strategy = Policy.WrapAsync(_retryPolicy, _circuitBreaker);
return await strategy.ExecuteAsync(async () =>
{
var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
return response;
});
}
// طريقة مبسطة للاستخدام
public async Task<string> GetStringAsync(string url)
{
var response = await SendAsync(new HttpRequestMessage(HttpMethod.Get, url));
return await response.Content.ReadAsStringAsync();
}
}
استراتيجية متقدمة: تبديل البروكسي عند الفشل
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using Polly;
public class FailoverProxyClient : IDisposable
{
private readonly List<string> _proxyEndpoints;
private int _currentProxyIndex = 0;
private HttpClient _httpClient;
public FailoverProxyClient(IEnumerable<string> proxyEndpoints)
{
_proxyEndpoints = new List<string>(proxyEndpoints);
_httpClient = CreateHttpClient(_proxyEndpoints[0]);
}
private HttpClient CreateHttpClient(string proxyUrl)
{
var handler = new SocketsHttpHandler
{
Proxy = new WebProxy(proxyUrl)
{
Credentials = new NetworkCredential("user-country-US", "PASSWORD")
},
UseProxy = true,
PooledConnectionLifetime = TimeSpan.FromMinutes(1)
};
return new HttpClient(handler)
{
Timeout = TimeSpan.FromSeconds(30)
};
}
public async Task<HttpResponseMessage> SendWithFailoverAsync(HttpRequestMessage request)
{
var policy = Policy<HttpResponseMessage>
.Handle<Exception>()
.RetryAsync(
retryCount: _proxyEndpoints.Count - 1,
onRetry: async (outcome, retryCount, context) =>
{
Console.WriteLine($"Switching to backup proxy (attempt {retryCount})");
// التبديل إلى البروكسي التالي
_currentProxyIndex = (_currentProxyIndex + 1) % _proxyEndpoints.Count;
// إعادة إنشاء HttpClient مع البروكسي الجديد
_httpClient?.Dispose();
_httpClient = CreateHttpClient(_proxyEndpoints[_currentProxyIndex]);
});
return await policy.ExecuteAsync(async () =>
{
var clonedRequest = await CloneRequestAsync(request);
return await _httpClient.SendAsync(clonedRequest);
});
}
private async Task<HttpRequestMessage> CloneRequestAsync(HttpRequestMessage request)
{
var clone = new HttpRequestMessage(request.Method, request.RequestUri);
if (request.Content != null)
{
var content = await request.Content.ReadAsByteArrayAsync();
clone.Content = new ByteArrayContent(content);
foreach (var header in request.Content.Headers)
{
clone.Content.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
}
foreach (var header in request.Headers)
{
clone.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
return clone;
}
public void Dispose()
{
_httpClient?.Dispose();
}
}
4. Parallel.ForEachAsync: كشط متزامن فعال
في .NET 8+، Parallel.ForEachAsync هي الطريقة المثلى للكشط المتزامن. توفر تحكمًا دقيقًا في درجة التوازي وتستخدم ValueTask بكفاءة.
using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
public class ConcurrentScraper
{
private readonly HttpClient _httpClient;
private readonly SemaphoreSlim _rateLimiter;
public ConcurrentScraper(int maxConcurrentRequests = 50)
{
var handler = new SocketsHttpHandler
{
Proxy = new WebProxy("http://gate.proxyhat.com:8080")
{
Credentials = new NetworkCredential("user-country-US", "PASSWORD")
},
UseProxy = true,
PooledConnectionLifetime = TimeSpan.FromMinutes(2),
MaxConnectionsPerServer = maxConcurrentRequests
};
_httpClient = new HttpClient(handler)
{
Timeout = TimeSpan.FromSeconds(30)
};
// تحديد عدد الطلبات المتزامنة
_rateLimiter = new SemaphoreSlim(maxConcurrentRequests, maxConcurrentRequests);
}
public async Task<Dictionary<string, ScrapingResult>> ScrapeUrlsAsync(
IEnumerable<string> urls,
CancellationToken cancellationToken = default)
{
var results = new ConcurrentDictionary<string, ScrapingResult>();
var errors = new ConcurrentBag<Exception>();
var options = new ParallelOptions
{
MaxDegreeOfParallelism = Environment.ProcessorCount * 4,
CancellationToken = cancellationToken
};
await Parallel.ForEachAsync(urls, options, async (url, ct) =>
{
await _rateLimiter.WaitAsync(ct);
try
{
var result = await ScrapeSingleUrlAsync(url, ct);
results.TryAdd(url, result);
}
catch (Exception ex)
{
errors.Add(ex);
results.TryAdd(url, new ScrapingResult
{
Url = url,
Success = false,
Error = ex.Message
});
}
finally
{
_rateLimiter.Release();
}
});
if (!errors.IsEmpty)
{
Console.WriteLine($"Completed with {errors.Count} errors");
}
return results.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
}
private async Task<ScrapingResult> ScrapeSingleUrlAsync(string url, CancellationToken ct)
{
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
var response = await _httpClient.GetAsync(url, ct);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync(ct);
stopwatch.Stop();
return new ScrapingResult
{
Url = url,
Success = true,
Content = content,
StatusCode = (int)response.StatusCode,
ResponseTimeMs = stopwatch.ElapsedMilliseconds,
ProxyUsed = response.Headers.Contains("X-Proxy-IP")
? response.Headers.GetValues("X-Proxy-IP").FirstOrDefault()
: "unknown"
};
}
}
public class ScrapingResult
{
public string Url { get; set; } = string.Empty;
public bool Success { get; set; }
public string Content { get; set; } = string.Empty;
public int StatusCode { get; set; }
public long ResponseTimeMs { get; set; }
public string? ProxyUsed { get; set; }
public string? Error { get; set; }
}
5. خدمة تجمع بروكسيات دوارة مع DI
للتطبيقات الإنتاجية، تحتاج إلى خدمة تدار تجمع بروكسيات مع حقن التبعية Dependency Injection. هذا النمط يسمح بتبديل IPs تلقائيًا وإدارة دورة حياة البروكسيات.
using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
// الإعدادات
public class ProxyPoolOptions
{
public string Gateway { get; set; } = "gate.proxyhat.com";
public int HttpPort { get; set; } = 8080;
public int SocksPort { get; set; } = 1080;
public string Username { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
// خيارات الجغرافيا
public string? DefaultCountry { get; set; }
public string? DefaultCity { get; set; }
// خيارات الدوران
public RotationStrategy Strategy { get; set; } = RotationStrategy.PerRequest;
public TimeSpan SessionDuration { get; set; } = TimeSpan.FromMinutes(5);
// حدود
public int MaxRetries { get; set; } = 3;
public int MaxConcurrentConnections { get; set; } = 100;
}
public enum RotationStrategy
{
PerRequest, // IP جديد لكل طلب
StickySession, // جلسة لزجة
RoundRobin // دوران بين بروكسيات محددة مسبقًا
}
// نموذج البروكسي
public class ProxyEndpoint
{
public string Host { get; set; } = string.Empty;
public int Port { get; set; }
public string? Username { get; set; }
public string? Password { get; set; }
public ProxyType Type { get; set; }
public string? Country { get; set; }
public string? City { get; set; }
public string? SessionId { get; set; }
public DateTime ExpiresAt { get; set; }
public bool IsHealthy { get; set; } = true;
public int FailedRequests { get; set; }
}
public enum ProxyType { Http, Socks5 }
// واجهة الخدمة
public interface IProxyPoolService
{
Task<HttpClient> GetClientAsync(string? country = null, string? city = null);
Task<ProxyEndpoint> GetNextProxyAsync();
void MarkProxyHealthy(ProxyEndpoint proxy);
void MarkProxyUnhealthy(ProxyEndpoint proxy, Exception? error = null);
Task<ProxyPoolStats> GetStatsAsync();
}
public class ProxyPoolStats
{
public int TotalProxies { get; set; }
public int HealthyProxies { get; set; }
public int ActiveConnections { get; set; }
public int RequestsServed { get; set; }
public int FailedRequests { get; set; }
}
// تنفيذ الخدمة
public class ProxyPoolService : IProxyPoolService, IDisposable
{
private readonly ILogger<ProxyPoolService> _logger;
private readonly ProxyPoolOptions _options;
private readonly ConcurrentBag<ProxyEndpoint> _proxies;
private readonly ConcurrentDictionary<string, HttpClient> _clients;
private readonly Random _random;
private int _requestCounter;
public ProxyPoolService(
IOptions<ProxyPoolOptions> options,
ILogger<ProxyPoolService> logger)
{
_options = options.Value;
_logger = logger;
_proxies = new ConcurrentBag<ProxyEndpoint>();
_clients = new ConcurrentDictionary<string, HttpClient>();
_random = new Random();
InitializeProxies();
}
private void InitializeProxies()
{
// إنشاء نقاط نهاية البروكسي بناءً على الاستراتيجية
for (int i = 0; i < 10; i++)
{
var sessionSuffix = _options.Strategy == RotationStrategy.StickySession
? $"-session-{Guid.NewGuid():N}"
: string.Empty;
var username = BuildUsername(sessionSuffix);
_proxies.Add(new ProxyEndpoint
{
Host = _options.Gateway,
Port = _options.HttpPort,
Username = username,
Password = _options.Password,
Type = ProxyType.Http,
Country = _options.DefaultCountry,
City = _options.DefaultCity,
ExpiresAt = DateTime.UtcNow.Add(_options.SessionDuration),
IsHealthy = true
});
}
}
private string BuildUsername(string sessionSuffix)
{
var parts = new List<string> { $"user{_options.Username}" };
if (!string.IsNullOrEmpty(_options.DefaultCountry))
parts.Add($"country-{_options.DefaultCountry}");
if (!string.IsNullOrEmpty(_options.DefaultCity))
parts.Add($"city-{_options.DefaultCity}");
parts.Add(sessionSuffix);
return string.Join("-", parts.Where(p => !string.IsNullOrEmpty(p)));
}
public async Task<HttpClient> GetClientAsync(string? country = null, string? city = null)
{
var proxy = await GetNextProxyAsync();
var clientKey = $"{proxy.Host}:{proxy.Port}:{proxy.SessionId ?? "default"}";
return _clients.GetOrAdd(clientKey, _ =>
{
var handler = new SocketsHttpHandler
{
Proxy = new WebProxy($"http://{proxy.Host}:{proxy.Port}")
{
Credentials = new NetworkCredential(proxy.Username, proxy.Password)
},
UseProxy = true,
PooledConnectionLifetime = _options.SessionDuration,
MaxConnectionsPerServer = _options.MaxConcurrentConnections
};
return new HttpClient(handler)
{
Timeout = TimeSpan.FromSeconds(30)
};
});
}
public Task<ProxyEndpoint> GetNextProxyAsync()
{
var healthyProxies = _proxies
.Where(p => p.IsHealthy && p.ExpiresAt > DateTime.UtcNow)
.ToList();
if (!healthyProxies.Any())
{
_logger.LogWarning("No healthy proxies available, refreshing pool...");
RefreshPool();
healthyProxies = _proxies.Where(p => p.IsHealthy).ToList();
}
// اختيار عشوائي أو دوران
ProxyEndpoint selected;
if (_options.Strategy == RotationStrategy.RoundRobin)
{
var index = Interlocked.Increment(ref _requestCounter) % healthyProxies.Count;
selected = healthyProxies[index];
}
else
{
selected = healthyProxies[_random.Next(healthyProxies.Count)];
}
return Task.FromResult(selected);
}
public void MarkProxyHealthy(ProxyEndpoint proxy)
{
proxy.IsHealthy = true;
proxy.FailedRequests = 0;
}
public void MarkProxyUnhealthy(ProxyEndpoint proxy, Exception? error = null)
{
proxy.FailedRequests++;
if (proxy.FailedRequests >= _options.MaxRetries)
{
proxy.IsHealthy = false;
_logger.LogError($"Proxy marked unhealthy: {proxy.Host}:{proxy.Port}. Error: {error?.Message}");
}
}
public Task<ProxyPoolStats> GetStatsAsync()
{
return Task.FromResult(new ProxyPoolStats
{
TotalProxies = _proxies.Count,
HealthyProxies = _proxies.Count(p => p.IsHealthy),
RequestsServed = _requestCounter
});
}
private void RefreshPool()
{
// إزالة البروكسيات منتهية الصلاحية
foreach (var proxy in _proxies.Where(p => p.ExpiresAt <= DateTime.UtcNow).ToList())
{
_proxies.TryTake(out _);
}
// إضافة بروكسيات جديدة
InitializeProxies();
}
public void Dispose()
{
foreach (var client in _clients.Values)
{
client.Dispose();
}
}
}
// تسجيل الخدمة في Program.cs
// builder.Services.Configure<ProxyPoolOptions>(builder.Configuration.GetSection("ProxyPool"));
// builder.Services.AddSingleton<IProxyPoolService, ProxyPoolService>();
6. TLS وشهادات SSL: التكوين المتقدم
عند العمل مع بروكسيات، خاصة تلك التي تقوم بفحص SSL/TLS، تحتاج إلى تكوين TLS بشكل صحيح.
تعطيل التحقق من الشهادة للتطوير فقط
using System;
using System.Net.Http;
using System.Security.Authentication;
public class DevProxyClient
{
private readonly HttpClient _httpClient;
public DevProxyClient()
{
var handler = new SocketsHttpHandler
{
Proxy = new WebProxy("http://gate.proxyhat.com:8080")
{
Credentials = new NetworkCredential("user-country-US", "PASSWORD")
},
UseProxy = true,
// تعطيل التحقق من الشهادة - للتطوير فقط!
SslOptions = new SslClientAuthenticationOptions
{
RemoteCertificateValidationCallback = (sender, cert, chain, errors) => true,
EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13
}
};
_httpClient = new HttpClient(handler);
}
public async Task<string> GetAsync(string url)
{
return await _httpClient.GetStringAsync(url);
}
}
Certificate Pinning للإنتاج
using System;
using System.Net.Http;
using System.Security.Cryptography.X509Certificates;
using System.Security.Authentication;
using System.Net.Security;
using System.Net;
using System.Linq;
public class SecureProxyClient
{
private readonly HttpClient _httpClient;
private readonly HashSet<string> _allowedCertificates;
public SecureProxyClient()
{
// بصمات الشهادات الموثوقة (SHA-256 thumbprints)
_allowedCertificates = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"A1B2C3D4E5F6...", // شهادة الموقع المستهدف
"FEDCBA987654..." // شهادة بروكسي ProxyHat
};
var handler = new SocketsHttpHandler
{
Proxy = new WebProxy("http://gate.proxyhat.com:8080")
{
Credentials = new NetworkCredential("user-country-US", "PASSWORD")
},
UseProxy = true,
SslOptions = new SslClientAuthenticationOptions
{
RemoteCertificateValidationCallback = ValidateCertificate,
EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13,
// شهادة CA مخصصة إذا لزم الأمر
ClientCertificates = new X509Certificate2Collection()
},
// إعدادات الاتصال الآمن
PooledConnectionLifetime = TimeSpan.FromMinutes(5),
EnableMultipleHttp2Connections = true
};
_httpClient = new HttpClient(handler);
}
private bool ValidateCertificate(
object sender,
X509Certificate? certificate,
X509Chain? chain,
SslPolicyErrors errors)
{
if (certificate == null) return false;
// السماح بأخطاء معينة إذا كانت الشهادة موثوقة
if (errors == SslPolicyErrors.None) return true;
// التحقق من بصمة الشهادة
var thumbprint = certificate.GetCertHashString();
if (_allowedCertificates.Contains(thumbprint))
{
Console.WriteLine($"Certificate pinned: {thumbprint}");
return true;
}
Console.WriteLine($"Certificate validation failed: {errors}, Thumbprint: {thumbprint}");
return false;
}
// تحميل شهادة CA مخصصة
public void LoadCustomRootCA(string certPath, string password)
{
var cert = new X509Certificate2(certPath, password);
var handler = new SocketsHttpHandler
{
Proxy = new WebProxy("http://gate.proxyhat.com:8080")
{
Credentials = new NetworkCredential("user-country-US", "PASSWORD")
},
UseProxy = true,
SslOptions = new SslClientAuthenticationOptions
{
RemoteCertificateValidationCallback = ValidateCertificate,
ClientCertificates = { cert },
EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13
}
};
}
public async Task<string> GetSecureAsync(string url)
{
var response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
مقارنة بين أنواع البروكسيات
| المعيار | بروكسيات سكنية Residential | بروكسيات مركز بيانات Datacenter | بروكسيات موبايل Mobile |
|---|---|---|---|
| السرعة | متوسطة | عالية جدًا | منخفضة-متوسطة |
| الاكتشاف | صعب | سهل | صعب جدًا |
| التكلفة | متوسطة-عالية | منخفضة | عالية جدًا |
| حالة الاستخدام | كشط SERP، مراقبة الأسعار | أتمتة سريعة، اختبار الأداء | وسائل التواصل، تطبيقات موبايل |
| معدل النجاح | 95-99% | 60-80% | 99%+ |
أفضل الممارسات
- إعادة استخدام HttpClient: لا تنشئ
HttpClientجديد لكل طلب. استخدمIHttpClientFactoryأو singleton client. - إدارة PooledConnectionLifetime: حدد قيمة مناسبة بناءً على سياسة دوران IP.
- معالجة الأخطاء: استخدم Polly لإعادة المحاولة مع التراجع الأسي.
- حدود المعدل: استخدم
SemaphoreSlimللتحكم في عدد الطلبات المتزامنة. - التسجيل: سجل كل طلب فاشل مع البروكسي المستخدم لتحليل المشاكل.
- الجلسات اللزجة: استخدم معرف جلسة ثابت للطلبات التي تتطلب نفس IP.
نقاط رئيسية
- استخدم
SocketsHttpHandlerفي .NET 8+ للحصول على أفضل أداء وتحكم.- حدد
PooledConnectionLifetimeلتجنب إعادة استخدام اتصالات بروكسي منتهية الصلاحية.- دمج Polly مع HttpClient لاستراتيجية إعادة محاولة ذكية و circuit breaker.
Parallel.ForEachAsyncهي الطريقة المثلى للكشط المتزامن في .NET 8.- بناء خدمة تجمع بروكسيات مع DI يسهل الإدارة والاختبار.
- قم بتكوين TLS بشكل صحيح مع certificate pinning للإنتاج.
للبدء مع C# residential proxies أو .NET HttpClient proxy، يمكنك التسجيل في ProxyHat والحصول على بيانات الاعتماد الخاصة بك. استخدم الكود user-country-US في اسم المستخدم للوصول إلى بروكسيات سكنية في الولايات المتحدة، أو استبدل بـ DE أو GB أو أي دولة أخرى حسب احتياجاتك.
لمزيد من الأدلة، راجع أفضل ممارسات كشط الويب و تتبع نتائج محركات البحث.






