【问题标题】:How to use Proxy with TcpClient.ConnectAsync()?如何将代理与 TcpClient.ConnectAsync() 一起使用?
【发布时间】:2016-05-06 03:40:11
【问题描述】:

.NET 中的 HTTP 代理支持实际上并不支持 TcpClient 或 Socket 等较低级别的类。但是我想通过支持'CONNECT'命令的HTTP代理连接一个TCPServer(ip,端口)。

所以我需要做以下步骤:

  1. 连接到代理。
  2. 发送CONNECT Host:Port HTTP/1.1<CR><LF>
  3. 发送<CR><LF>
  4. 等待响应行。如果包含HTTP/1.X 200,则连接成功。
  5. 阅读更多响应行,直到收到空行。
  6. 它通过代理连接到外部世界。使用代理进行任何可能的数据交换。

其实我这样做没有代理

    TcpClient _client;
    NetworkStream _stream;

    public static async Task<bool> ConnectAsync(string hostname, int port)
    {
        _client = new TcpClient();
        await _client.ConnectAsync(hostname, port).ConfigureAwait(false);
        _stream = conn._client.GetStream();

        ..... Do some stuff

        // Connexion OK
        return true;
    }

在连接 TcpClient 之前如何使用代理和凭据?

【问题讨论】:

    标签: c# .net networking proxy tcpclient


    【解决方案1】:

    我找到了基于.NET: Connecting a TcpClient through an HTTP proxy with authentication 和Bypass the proxy using TcpClient 的解决方案

    TcpClient _client;
    NetworkStream _stream;
    
    public TcpClient ProxyTcpClient(string targetHost, int targetPort, string httpProxyHost, int httpProxyPort, string proxyUserName, string proxyPassword)
    {
            const BindingFlags Flags = BindingFlags.NonPublic | BindingFlags.Instance;
            Uri proxyUri = new UriBuilder
            {
                Scheme = Uri.UriSchemeHttp,
                Host = httpProxyHost,
                Port = httpProxyPort
            }.Uri;
            Uri targetUri = new UriBuilder
            {
                 Scheme = Uri.UriSchemeHttp,
                 Host = targetHost,
                 Port = targetPort
            }.Uri;
    
            WebProxy webProxy = new WebProxy(proxyUri, true);
            webProxy.Credentials = new NetworkCredential(proxyUserName, proxyPassword);
            WebRequest request = WebRequest.Create(targetUri);
            request.Proxy = webProxy;
            request.Method = "CONNECT";
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            Stream responseStream = response.GetResponseStream();
            Type responseType = responseStream.GetType();
            PropertyInfo connectionProperty = responseType.GetProperty("Connection", Flags);
            var connection = connectionProperty.GetValue(responseStream, null);
            Type connectionType = connection.GetType();
            PropertyInfo networkStreamProperty = connectionType.GetProperty("NetworkStream", Flags);
            NetworkStream networkStream = (NetworkStream)networkStreamProperty.GetValue(connection, null);
            Type nsType = networkStream.GetType();
            PropertyInfo socketProperty = nsType.GetProperty("Socket", Flags);
            Socket socket = (Socket)socketProperty.GetValue(networkStream, null);
    
            return new TcpClient { Client = socket };
    }
    
    public static async Task<bool> ConnectAsync(string hostname, int port)
    {
            _client = ProxyTcpClient("IPTargetHost", 1234, "IPProxyHost", 5678, "Userproxy", "Userppwd");
            _stream = conn._client.GetStream();
    
            ..... Do some stuff
    
            // Connexion OK
            return true;
    }
    

    【讨论】:

    • 这个解决方案似乎有点老套......有没有其他方法可以在不使用反射的情况下设置代理?
    【解决方案2】:

    我们已经设法使用 .Net 的Socket 来实现它。 Nuget 包称为Filemail.ProxiedTcpClient。代码很简单:

    public static TcpClient CreateProxied(Uri proxy, Uri destination)
    {
        var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    
        socket.Connect(proxy.Host, proxy.Port);
    
        var connectMessage = Encoding.UTF8.GetBytes($"CONNECT {destination.Host}:{destination.Port} HTTP/1.1{Environment.NewLine}{Environment.NewLine}");
        socket.Send(connectMessage);
    
        byte[] receiveBuffer = new byte[1024];
        var received = socket.Receive(receiveBuffer);
    
        var response = ASCIIEncoding.ASCII.GetString(receiveBuffer, 0, received);
    
        if (!response.Contains("200 OK"))
        {
            throw new Exception($"Error connecting to proxy server {destination.Host}:{destination.Port}. Response: {response}");
        }
    
        return new TcpClient
        {
            Client = socket
        };
    }
    

    在这里投稿:https://github.com/filemail/ProxiedTcpClient

    【讨论】:

      【解决方案3】:

      如果代理需要基本身份验证,要扩展 Rychu's 答案,请将标头添加到连接消息中

      var auth = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{proxy.Login}:{proxy.Password}"));
      var connectMessage = Encoding.UTF8.GetBytes($"CONNECT {host}:{port} HTTP/1.1\nProxy-Authorization: Basic {auth}\n\n");
      socket.Send(connectMessage);
      

      【讨论】:

        猜你喜欢
        • 2019-01-30
        • 1970-01-01
        • 2016-07-16
        • 2014-05-19
        • 2014-10-25
        • 2021-05-22
        • 2013-05-27
        • 2012-11-06
        • 2011-04-21
        相关资源
        最近更新 更多