【问题标题】:Use WebClient with socks proxy将 WebClient 与 socks 代理一起使用
【发布时间】:2012-10-18 19:28:10
【问题描述】:

有没有办法通过WebClient 使用 socks 代理?特别是它提供的DownloadString 方法?

我不想使用任何第三方的东西,比如 privoxy、freecap 等等,我不能使用像 Chilkat 那样的商业库。我尝试使用来自 http://www.mentalis.org/ 的东西,实际上我使用了他们的 WebRequest 实现,但它们似乎没有类似的 WebClient 功能。

【问题讨论】:

    标签: c# .net webclient socks webclient-download


    【解决方案1】:

    WebRequest/WebResponse 类和扩展的 WebClient 类不直接支持SOCKS(它依赖于WebRequest 来完成其工作)。

    确实不能,因为它在传输层 (TCP/IP) 上工作,而不是通过简单的重定向到转发 HTTP 请求的服务器(这是 WebRequest/WebResponse 类工作的级别开)。

    您可以创建一个specialized derivation of WebRequest/WebResponse(它使用ProxySocket 来处理低级握手,然后),然后创建一个专门的WebClient class 覆盖GetWebRequest 和GetWebResponse 方法。

    一旦你用那个类代替了你的 WebClient 实例,它应该可以正常工作(但你可能必须在每次使用它的情况下设置代理)。

    【讨论】:

      【解决方案2】:

      这是我最终的做法,谢谢 casperOne 的回答

      public class SocksWebClient : WebClient
          {
              public IProxyDetails ProxyDetails { get; set; }
              public string UserAgent { get; set; }
      
              protected override WebRequest GetWebRequest(Uri address)
              {
                  WebRequest result = null;
      
                  if (ProxyDetails != null)
                  {
                      if (ProxyDetails.ProxyType == ProxyType.Proxy)
                      {
                          result = (HttpWebRequest)WebRequest.Create(address);
                          result.Proxy = new WebProxy(ProxyDetails.FullProxyAddress);
                          if (!string.IsNullOrEmpty(UserAgent))
                              ((HttpWebRequest)result).UserAgent = UserAgent;
                      }
                      else if (ProxyDetails.ProxyType == ProxyType.Socks)
                      {
                          result = SocksHttpWebRequest.Create(address);
                          result.Proxy = new WebProxy(ProxyDetails.FullProxyAddress);
                          //TODO: implement user and password
      
                      }
                      else if (ProxyDetails.ProxyType == ProxyType.None)
                      {
                          result = (HttpWebRequest)WebRequest.Create(address);
                          if (!string.IsNullOrEmpty(UserAgent))
                              ((HttpWebRequest)result).UserAgent = UserAgent;
                      }
                  }
                  else
                  {
                      result = (HttpWebRequest)WebRequest.Create(address);
                      if (!string.IsNullOrEmpty(UserAgent))
                          ((HttpWebRequest)result).UserAgent = UserAgent;
                  }
      
      
                  return result;
              }
      
          }
      

      SocksHttpWebRequest类取自@casperOne所链接的blog,其代码如下:

      using System;
      using System.Collections.Specialized;
      using System.IO;
      using System.Net;
      using System.Net.Sockets;
      using System.Text;
      using Org.Mentalis.Network.ProxySocket;
      
      namespace Ditrans
      {
          public class SocksHttpWebRequest : WebRequest
          {
      
              #region Member Variables
      
              private readonly Uri _requestUri;
              private WebHeaderCollection _requestHeaders;
              private string _method;
              private SocksHttpWebResponse _response;
              private string _requestMessage;
              private byte[] _requestContentBuffer;
      
              // darn MS for making everything internal (yeah, I'm talking about you, System.net.KnownHttpVerb)
              static readonly StringCollection validHttpVerbs =
                  new StringCollection { "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "OPTIONS" };
      
              #endregion
      
              #region Constructor
      
              private SocksHttpWebRequest(Uri requestUri)
              {
                  _requestUri = requestUri;
              }
      
              #endregion
      
              #region WebRequest Members
      
              public override WebResponse GetResponse()
              {
                  if (Proxy == null)
                  {
                      throw new InvalidOperationException("Proxy property cannot be null.");
                  }
                  if (String.IsNullOrEmpty(Method))
                  {
                      throw new InvalidOperationException("Method has not been set.");
                  }
      
                  if (RequestSubmitted)
                  {
                      return _response;
                  }
                  _response = InternalGetResponse();
                  RequestSubmitted = true;
                  return _response;
              }
      
              public override Uri RequestUri
              {
                  get { return _requestUri; }
              }
      
              public override IWebProxy Proxy { get; set; }
      
              public override WebHeaderCollection Headers
              {
                  get
                  {
                      if (_requestHeaders == null)
                      {
                          _requestHeaders = new WebHeaderCollection();
                      }
                      return _requestHeaders;
                  }
                  set
                  {
                      if (RequestSubmitted)
                      {
                          throw new InvalidOperationException("This operation cannot be performed after the request has been submitted.");
                      }
                      _requestHeaders = value;
                  }
              }
      
              public bool RequestSubmitted { get; private set; }
      
              public override string Method
              {
                  get
                  {
                      return _method ?? "GET";
                  }
                  set
                  {
                      if (validHttpVerbs.Contains(value))
                      {
                          _method = value;
                      }
                      else
                      {
                          throw new ArgumentOutOfRangeException("value", string.Format("'{0}' is not a known HTTP verb.", value));
                      }
                  }
              }
      
              public override long ContentLength { get; set; }
      
              public override string ContentType { get; set; }
      
              public override Stream GetRequestStream()
              {
                  if (RequestSubmitted)
                  {
                      throw new InvalidOperationException("This operation cannot be performed after the request has been submitted.");
                  }
      
                  if (_requestContentBuffer == null)
                  {
                      _requestContentBuffer = new byte[ContentLength];
                  }
                  else if (ContentLength == default(long))
                  {
                      _requestContentBuffer = new byte[int.MaxValue];
                  }
                  else if (_requestContentBuffer.Length != ContentLength)
                  {
                      Array.Resize(ref _requestContentBuffer, (int) ContentLength);
                  }
                  return new MemoryStream(_requestContentBuffer);
              }
      
              #endregion
      
              #region Methods
      
              public static new WebRequest Create(string requestUri)
              {
                  return new SocksHttpWebRequest(new Uri(requestUri));
              }
      
              public static new WebRequest Create(Uri requestUri)
              {
                  return new SocksHttpWebRequest(requestUri);
              }
      
              private string BuildHttpRequestMessage()
              {
                  if (RequestSubmitted)
                  {
                      throw new InvalidOperationException("This operation cannot be performed after the request has been submitted.");
                  }
      
                  var message = new StringBuilder();
                  message.AppendFormat("{0} {1} HTTP/1.0\r\nHost: {2}\r\n", Method, RequestUri.PathAndQuery, RequestUri.Host);
      
                  // add the headers
                  foreach (var key in Headers.Keys)
                  {
                      message.AppendFormat("{0}: {1}\r\n", key, Headers[key.ToString()]);
                  }
      
                  if (!string.IsNullOrEmpty(ContentType))
                  {
                      message.AppendFormat("Content-Type: {0}\r\n", ContentType);
                  }
                  if (ContentLength > 0)
                  {
                      message.AppendFormat("Content-Length: {0}\r\n", ContentLength);
                  }
      
                  // add a blank line to indicate the end of the headers
                  message.Append("\r\n");
      
                  // add content
                  if(_requestContentBuffer != null && _requestContentBuffer.Length > 0)
                  {
                      using (var stream = new MemoryStream(_requestContentBuffer, false))
                      {
                          using (var reader = new StreamReader(stream))
                          {
                              message.Append(reader.ReadToEnd());
                          }
                      }
                  }
      
                  return message.ToString();
              }
      
              private SocksHttpWebResponse InternalGetResponse()
              {
                  var response = new StringBuilder();
                  using (var _socksConnection =
                      new ProxySocket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
                  {
                      var proxyUri = Proxy.GetProxy(RequestUri);
                      var ipAddress = GetProxyIpAddress(proxyUri);
                      _socksConnection.ProxyEndPoint = new IPEndPoint(ipAddress, proxyUri.Port);
                      _socksConnection.ProxyType = ProxyTypes.Socks5;
      
                      // open connection
                      _socksConnection.Connect(RequestUri.Host, 80);
                      // send an HTTP request
                      _socksConnection.Send(Encoding.ASCII.GetBytes(RequestMessage));
                      // read the HTTP reply
                      var buffer = new byte[1024];
      
                      var bytesReceived = _socksConnection.Receive(buffer);
                      while (bytesReceived > 0)
                      {
                          response.Append(Encoding.ASCII.GetString(buffer, 0, bytesReceived));
                          bytesReceived = _socksConnection.Receive(buffer);
                      }
                  }
                  return new SocksHttpWebResponse(response.ToString());
              }
      
              private static IPAddress GetProxyIpAddress(Uri proxyUri)
              {
                  IPAddress ipAddress;
                  if (!IPAddress.TryParse(proxyUri.Host, out ipAddress))
                  {
                      try
                      {
                          return Dns.GetHostEntry(proxyUri.Host).AddressList[0];
                      }
                      catch (Exception e)
                      {
                          throw new InvalidOperationException(
                              string.Format("Unable to resolve proxy hostname '{0}' to a valid IP address.", proxyUri.Host), e);
                      }
                  }
                  return ipAddress;
              }
      
              #endregion
      
              #region Properties
      
              public string RequestMessage
              {
                  get
                  {
                      if (string.IsNullOrEmpty(_requestMessage))
                      {
                          _requestMessage = BuildHttpRequestMessage();
                      }
                      return _requestMessage;
                  }
              }
      
              #endregion
      
          }
      }
      

      请注意,正如 @casperOne 所指出的,这使用了一个名为 ProxySocket 的(免费)第三方库。

      【讨论】:

      • 17 个人还没有试用过这段代码。如果是这样,他们会注意到这个答案是不完整的,因为这里缺少接口和其他类的代码。
      • 我试过用谷歌搜索它们,但即使在那里你也找不到。不幸的是,这种方式毫无用处。
      • @C4u 这一点也不无用——如果您阅读代码,您会发现 IProxyDetails 是一个简单的接口,上面有一些属性,您可以轻松计算出它们的类型。缺少的是 SocksHttpWebRequest 的实现,OP 从接受的答案中链接的博客中获取。快速编辑将解决此问题。
      【解决方案3】:

      我也遇到了这个,发现了不错的BetterHttpClient

      它派生自 WebClient 并允许您指定 socks 代理:

      BetterHttpClient.HttpClient client = new BetterHttpClient.HttpClient(new BetterHttpClient.Proxy("IP address", port, BetterHttpClient.ProxyTypeEnum.Socks));
      

      【讨论】:

      • 这比接受的答案和更高的推导答案要好得多。这个适用于简单的 NuGet 包并适用于 HTTPS。不要试图找出代码的缺失部分,或者让它只适用于 HTTP。
      • BetterHttpClient 存在故障且已弃用。不要使用它
      【解决方案4】:

      为了做到这一点,我一直在寻找图书馆。最后我找到了MihaZupan/HttpToSocks5Proxy,它是真正的救星。就像这样你可以使用它:

      using MihaZupan;
      
      var proxy = new HttpToSocks5Proxy("127.0.0.1", 1080,
         "username", "password" // optional
      );
      var handler = new HttpClientHandler { Proxy = proxy };
      HttpClient httpClient = new HttpClient(handler, true);
      
      var result = await httpClient.SendAsync(
          new HttpRequestMessage(HttpMethod.Get, "https://httpbin.org/ip"));
      
      Console.WriteLine("HTTPS GET: " + await result.Content.ReadAsStringAsync());
      

      如果您想要更方便的WebClient-esque API 来读取内容,您可以将 response-read 调用包装到扩展方法中:

      public static class ExtensionMethods
      {
        public static async Task<string> DownloadStringAsync(this HttpClient client, string url)
        {
          var response = await client.SendAsync(new HttpRequestMessage(
            HttpMethod.Get, url));
      
          return await response.Content.ReadAsStringAsync();
        }
      }
      

      【讨论】:

        【解决方案5】:

        对于未来的读者: 从 .NET 6 开始,socks 代理是 supported 原生的 HttpClient。

        注意:

        在撰写此答案时,WebClient 被视为已过时。应该改用HttpClient。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-05-19
          • 2021-05-22
          • 2012-11-06
          • 2019-07-18
          • 1970-01-01
          • 2015-05-05
          • 2010-12-19
          相关资源
          最近更新 更多