我也遇到了 SslStream 在超时后读取时返回五个字节的垃圾数据的问题,我单独提出了一个类似于 OP 的更新 #3 的解决方案。
我创建了一个包装器类,它包装了 Tcp NetworkStream 对象,因为它被传递到 SslStream 构造函数中。包装类将所有调用传递到底层 NetworkStream,除了 Read() 方法包含一个额外的 try...catch 以抑制 Timeout 异常并返回 0 字节。
SslStream 在这种情况下可以正常工作,包括在套接字关闭时引发相应的 IOException。请注意,我们从 Read() 返回 0 的 Stream 与从 Read() 返回 0 的 TcpClient 或 Socket 不同(这通常意味着套接字断开连接)。
class SocketTimeoutSuppressedStream : Stream
{
NetworkStream mStream;
public SocketTimeoutSuppressedStream(NetworkStream pStream)
{
mStream = pStream;
}
public override int Read(byte[] buffer, int offset, int count)
{
try
{
return mStream.Read(buffer, offset, count);
}
catch (IOException lException)
{
SocketException lInnerException = lException.InnerException as SocketException;
if (lInnerException != null && lInnerException.SocketErrorCode == SocketError.TimedOut)
{
// Normally, a simple TimeOut on the read will cause SslStream to flip its lid
// However, if we suppress the IOException and just return 0 bytes read, this is ok.
// Note that this is not a "Socket.Read() returning 0 means the socket closed",
// this is a "Stream.Read() returning 0 means that no data is available"
return 0;
}
throw;
}
}
public override bool CanRead => mStream.CanRead;
public override bool CanSeek => mStream.CanSeek;
public override bool CanTimeout => mStream.CanTimeout;
public override bool CanWrite => mStream.CanWrite;
public virtual bool DataAvailable => mStream.DataAvailable;
public override long Length => mStream.Length;
public override IAsyncResult BeginRead(byte[] buffer, int offset, int size, AsyncCallback callback, object state) => mStream.BeginRead(buffer, offset, size, callback, state);
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int size, AsyncCallback callback, object state) => mStream.BeginWrite(buffer, offset, size, callback, state);
public void Close(int timeout) => mStream.Close(timeout);
public override int EndRead(IAsyncResult asyncResult) => mStream.EndRead(asyncResult);
public override void EndWrite(IAsyncResult asyncResult) => mStream.EndWrite(asyncResult);
public override void Flush() => mStream.Flush();
public override Task FlushAsync(CancellationToken cancellationToken) => mStream.FlushAsync(cancellationToken);
public override long Seek(long offset, SeekOrigin origin) => mStream.Seek(offset, origin);
public override void SetLength(long value) => mStream.SetLength(value);
public override void Write(byte[] buffer, int offset, int count) => mStream.Write(buffer, offset, count);
public override long Position
{
get { return mStream.Position; }
set { mStream.Position = value; }
}
public override int ReadTimeout
{
get { return mStream.ReadTimeout; }
set { mStream.ReadTimeout = value; }
}
public override int WriteTimeout
{
get { return mStream.WriteTimeout; }
set { mStream.WriteTimeout = value; }
}
}
然后可以通过在将 TcpClient NetworkStream 对象传递给 SslStream 之前对其进行包装来使用它,如下所示:
NetworkStream lTcpStream = lTcpClient.GetStream();
SocketTimeoutSuppressedStream lSuppressedStream = new SocketTimeoutSuppressedStream(lTcpStream);
using (lSslStream = new SslStream(lSuppressedStream, true, ServerCertificateValidation, SelectLocalCertificate, EncryptionPolicy.RequireEncryption))
问题归结为 SslStream 在底层流发生任何异常时破坏其内部状态,即使是无害的超时。奇怪的是,下一个 read() 返回的五个(左右)字节数据实际上是来自网络的 TLS 加密有效负载数据的开始。
希望对你有帮助