进行 HTTP 隧道时如何保持连接打开

Posted

技术标签:

【中文标题】进行 HTTP 隧道时如何保持连接打开【英文标题】:How to keep connection open when doing HTTP tunneling 【发布时间】:2018-01-13 13:02:43 【问题描述】:

我连接到代理并使用连接命令发送一些自定义标头。这是一个要求。我收到 200 条回复。然后我尝试使用相同的连接来执行获取请求(搜索“GET 0”的附加代码),但我总是收到一个错误,最终导致“连接关闭”(无法回忆确切的错误)。本质上,我需要通过隧道连接到 https:\www.somesecuresite.com 之类的网站,这是代码。为简洁起见,排除了某些部分。

using (TcpClient client = new TcpClient(proxy, proxyPort))
            
                using (NetworkStream stream = client.GetStream())
                
                    string EncodedData = encodeUIDPWD(UserName, Password);
                    #region Establish Tcp tunnel

                    string reqString = "CONNECT 0:1 HTTP/1.1\r\nProxy-Authorization:Basic " + EncodedData + "\r\nHost: 2:3\r\n";
                    reqString += "Proxy-Connection: keep-alive\r\n";
                    reqString += "Connection: keep-alive\r\n";
                    reqString += "Header1: " + header1 + "\r\n";
                    reqString += "Header2: " + header2 + "\r\n"; 
                    reqString += "None: " + None + "\r\n\r\n";

                    string rString = String.Format(reqString, myUri.Host, myUri.Port, myUri.Host, myUri.Port);
                    #endregion
                    ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
                    string reqConnectResult = await DoRequest(myUri, stream, rString);

                    lines.AddRange(reqConnectResult.Split(new string[]  Environment.NewLine , StringSplitOptions.None).ToList());

                    if (!lines[0].Contains("200"))
                    
                         //Error code gets caterd for here e.g 503 etc
                    
                    else
                    
                        foreach (string line in lines)
                        
                            if (line.Contains("X-ProxyMesh-IP: "))
                            
                                ip = line.Replace("X-ProxyMesh-IP: ", string.Empty);
                            
                        

                        string reqString1 = "GET 0 HTTP/1.1\r\nHost: 1:2\r\n";
                        reqString1 += "Proxy-Connection: keep-alive\r\n";
                        reqString1 += "Connection: keep-alive\r\n\r\n";

                        string rString1 = string.Format(reqString1, myUri.PathAndQuery, myUri.Host, myUri.Port);
                        string reqPageResult = await DoRequest(myUri, stream, rString1);


                        lines.AddRange(reqPageResult.Split(new string[]  Environment.NewLine , StringSplitOptions.None).ToList());
                        response.Content = new StringContent(lines.ToString());

                        if (lines[0].Contains("200"))
                         
                            return new Tuple<bool, HttpResponseMessage>(true, response);
                        
                        else
                        
                            return new Tuple<bool, HttpResponseMessage>(false, response);
                        
                    
                
            

 private async Task<string> DoRequest(Uri myUri, NetworkStream stream, string reqString)


    byte[] tunnelRequest = Encoding.UTF8.GetBytes(reqString);

    await stream.WriteAsync(tunnelRequest, 0, tunnelRequest.Length);
    await stream.FlushAsync();


    using (var memory = new MemoryStream())
    
        await stream.CopyToAsync(memory);

        memory.Position = 0;
        var data = memory.ToArray();

        //Basically just gets the header part.
        int bm = BinaryMatch(data, Encoding.ASCII.GetBytes("\r\n\r\n"));
        var index = bm + 4;
        if (bm == -1)
        
            index = 0;
        

        var headers = Encoding.ASCII.GetString(data, 0, index);
        memory.Position = index;

        Console.WriteLine(headers);
        if (headers.IndexOf("Content-Encoding: gzip") > 0)
        
            using (GZipStream decompressionStream = new GZipStream(memory, CompressionMode.Decompress))
            using (var decompressedMemory = new MemoryStream())
            
                decompressionStream.CopyTo(decompressedMemory);
                decompressedMemory.Position = 0;
                string s = (Encoding.UTF8.GetString(decompressedMemory.ToArray()));
                Console.WriteLine(s);

                return headers + s;
            
        
        else
        
            string s = (Encoding.UTF8.GetString(data, index, data.Length - index));
            Console.WriteLine(s);

            return headers + s;
        
    
           

【问题讨论】:

服务器是否以Connection: keep-alive 的标头响应?如果不是,那么它可能不支持它.. 还有什么理由您手动执行此操作而不使用System.Net.WebRequest This 可能会有所帮助。 【参考方案1】:

Http 连接 由于其在特定时间后自动关闭连接的性质而关闭,除了增加连接的自动关闭持续时间(这又取决于服务器到服务器)之外,您无能为力。最终导致连接超时。

FTP 连接如果您想上传或下载需要大量时间的数据,最好使用仅出于相同原因而存在的 FTP 连接。

【讨论】:

【参考方案2】:

根据您的标头的性质,您可以考虑使用WebSockets,因为您可以发送custom authorization headers。

因为 WebSocket 旨在用于同一连接应允许全双工通信(数据下载和上传)时使用,所以它比 HTTP 连接更合适。

【讨论】:

以上是关于进行 HTTP 隧道时如何保持连接打开的主要内容,如果未能解决你的问题,请参考以下文章

http隧道的研究

停止 *** 隧道不会断开 iOS 中的 *** 连接

如何通过 http 隧道 Red5 而不会出现 NetConnection.Connect.Closed 错误?

开始使用 http 隧道

如何使用 python 打开 SSH 隧道?

Apache HttpClient 4.3.1 中使用 HTTP 隧道/HTTPS 连接的抢先式代理身份验证