如何在TcpClient.ConnectAsync()中使用Proxy?

时间:2016-01-28 16:36:53

标签: c# .net networking proxy tcpclient

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

所以我需要执行以下步骤:

  1. 连接到代理。
  2. 发送CONNECT Host:Port HTTP/1.1<CR><LF>
  3. 发送<CR><LF>
  4. 等待回复。如果它包含HTTP/1.X 200,则连接成功。
  5. 阅读更多响应行,直到收到空行。
  6. 它通过代理连接到外部世界。与代理无关的任何数据交换。
  7. 其实我这样做没有代理

        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之前使用代理和凭据?

3 个答案:

答案 0 :(得分:4)

我找到了基于.NET: Connecting a TcpClient through an HTTP proxy with authenticationBypass 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;
}

答案 1 :(得分:2)

如果代理需要基本身份验证,要扩展 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);

答案 2 :(得分:1)

我们设法使用.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