我们正在使用TcpClient和TcpListener在客户端和服务器应用程序之间进行通信。在某些情况下,我们必须支持通过代理服务器进行连接,因此我们已经成功实现了this article中的“隧道”方法。
最近,我们已“改进”我们的通信以使用SslStream,而不是通过Internet发送未加密的数据,但这似乎已经破坏了代理隧道。
我们的代码的摘要版本如下:
private bool Connect()
{
var uriBuilder = new UriBuilder
{
Scheme = Uri.UriSchemeHttp,
Host = _proxyServerHost,
Port = _proxyServerPort
};
var proxyUri = uriBuilder.Uri;
var request = WebRequest.Create(
"http://" + _targetHost + ":" + _targetPort);
var webProxy = new WebProxy(proxyUri);
request.Proxy = webProxy;
request.Method = "CONNECT";
if (_proxyUserName == null)
{
var credentials = new NetworkCredential(
_proxyUserName, _proxyPassword);
webProxy.Credentials = credentials;
}
HttpWebResponse response;
logger.InfoExt("{83CAAA66-3004-4C13-8F9A-5B1E23063E53}", $"Sending CONNECT command to the proxy server");
try
{
response = (HttpWebResponse)request.GetResponse();
}
catch (Exception ex)
{
logger.Error(ex, "{DF8498B5-A4CC-49ED-A928-BCB6D7D087D6}", "Error getting HTTP Response");
return false;
}
if (response.StatusCode != HttpStatusCode.OK)
{
logger.ErrorExt("{2D4040D6-9477-479E-B25D-2DBB58273265}",
$"Not able to connect to proxy server. Response code: {response.StatusCode}");
ConnectionStatus = eSocketConnectionStatus.NotConnected;
return false;
}
logger.InfoExt("{1E522215-3DB0-4ED7-8E6A-E2AD1AFD82D9}", $"Connected to the proxy server");
var responseStream = response.GetResponseStream();
const BindingFlags Flags = BindingFlags.NonPublic | BindingFlags.Instance;
var rsType = responseStream.GetType();
var connectionProperty = rsType.GetProperty("Connection", Flags);
var connection = connectionProperty.GetValue(responseStream, null);
var connectionType = connection.GetType();
var networkStreamProperty = connectionType.GetProperty("NetworkStream", Flags);
var networkStream = networkStreamProperty.GetValue(connection, null);
var nsType = networkStream.GetType();
var socketProperty = nsType.GetProperty("Socket", Flags);
var socket = (Socket)socketProperty.GetValue(networkStream, null);
_tcpClient = new TcpClient { Client = socket };
var sslStream = new SslStream(_tcpClient.GetStream(), true,
new RemoteCertificateValidationCallback(ValidateServerCertificate), null);
try
{
sslStream.AuthenticateAsClient(_targetHost);
}
catch (Exception ex)
{
logger.Error(ex, "{CA1D5BDB-B85D-41AE-B63D-1F4804765FC6}", "Error authenticating SSL stream");
_tcpClient.Close();
ConnectionStatus = eSocketConnectionStatus.ConnectionFailedToInitiate;
return false;
}
_tcpClientStream = sslStream;
pfnCallBack = new AsyncCallback(OnDataRecevied);
_tcpClientStream.BeginRead(receiveBuffer, 0, receiveBuffer.Length, pfnCallBack, null);
return true;
}
private void OnDataReceived(IAsyncResult asyn)
{
var iRx = _tcpClientStream.EndRead(asyn);
if (iRx <= 0)
{
// ERROR so disconnect
return;
}
}
我们遇到的问题是,当我们调用BeginRead时,回调立即被调用,EndRead返回0字节,根据MSDN文档,这意味着套接字已关闭,因此我们结束了连接。
如果不使用
_tcpClientStream = sslStream;
我们使用
_tcpClientStream = _tcpClient.GetStream();
(即我们不使用SSL),然后代码可以正常运行,并且没有问题。
有人可以提供有关此主题的任何建议吗?
“启发”我们方法的代码块似乎非常流行(出现在众多Github存储库,其他Stackoverflow问题等中),所以我很有信心,这通常是正确的方法? >