数据加密在通讯环应用是必不可少的,TLS加密通讯则是现有通讯的加密标准,可以使用.NETCore带的SslStream来集成这一功能;但SslStream的读写操作功能有限,在组件设计中不仅让SslStream使用PipeStream,同时也可以让SslStream直接转换成PipeStream进行更简便的数据流操作。
扩展
为了更好地让SslStream和PipeStream相结合操作,所以对SslStream扩展出一个新的类SslStreamX,通过它可以更好的在流数据处理隐藏SslStream和PipeStream之间的操作处理。
class SslStreamX : SslStream
{
//构建SslStreamX,并指写来源的缓冲池,编码,存储字序和基础的数据流
public SslStreamX(IBufferPool pool, Encoding encoding, bool littleEndian, Stream innerStream, bool leaveInnerStreamOpen)
: base(innerStream, leaveInnerStreamOpen)
{
BufferPool = pool;
Encoding = encoding;
LittleEndian = littleEndian;
mPipeStream = new PipeStream(BufferPool, LittleEndian, Encoding);
mPipeStream.FlashCompleted = OnWriterFlash;
mPipeStream.InnerStream = this;
}
//构建SslStreamX,并指写来源的缓冲池,编码,存储字序,基础的数据流和验证回调方法
public SslStreamX(
IBufferPool pool, Encoding encoding, bool littleEndian, Stream innerStream, RemoteCertificateValidationCallback callback)
: base(innerStream, false, callback, null)
{
BufferPool = pool;
Encoding = encoding;
LittleEndian = littleEndian;
mPipeStream = new PipeStream(BufferPool, LittleEndian, Encoding);
mPipeStream.FlashCompleted = OnWriterFlash;
mPipeStream.InnerStream = this;
}
public IBufferPool BufferPool { get; set; }
public Encoding Encoding { get; set; }
public bool LittleEndian { get; set; }
private PipeStream mPipeStream;
//提交数据到SslStream
private void OnWriterFlash(Buffers.IBuffer data)
{
StreamHelper.WriteBuffer(this, data);
}
//提交数据
public override void Flush()
{
if (mPipeStream != null)
mPipeStream.Flush();
base.Flush();
}
//获取当前SslStream对应的PipeStream
public PipeStream GetPipeStream()
{
return mPipeStream;
}
public Exception SyncDataError { get; set; }
public bool AsyncDataStatus { get; set; }
//同步接收的数据到PipeStream中
public async void SyncData(Action receive)
{
while (true)
{
var dest = GetPipeStream();
IBuffer buffer = null;
try
{
buffer = BufferPoolGroup.DefaultGroup.Next().Pop();
int rlen = await ReadAsync(buffer.Data, 0, buffer.Size);
if (rlen > 0)
{
buffer.SetLength(rlen);
dest.Import(buffer);
}
else
{
buffer.Free();
SyncDataError = new BXException("ssl receive null data!");
break;
}
}
catch (Exception e_)
{
SyncDataError = e_;
buffer?.Free();
break;
}
finally
{
receive?.Invoke();
}
}
}
//释放流
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (mPipeStream != null)
{
mPipeStream.Dispose();
mPipeStream = null;
}
}
}
(https://github.com/IKende/BeetleX/blob/master/src/BeetleX/Buffers/SslStramX.cs)
由于实现机制上的差异,SslStreamX的读写数据流是不建议直接调用SslStream的Read/Write方法进行;而是通过GetPipeStream获取内部的PipeStream进行一个写入操作;实际上PipeStream的写入并没有直接提交给SslStreamX,而是当SslStreamX提交的时候再把写入的数据流一次过提交给SslStream。
数据同步
SslStreamX提供了一个同步数据的方法SyncData,主要是通过异步读取基础数据流,如果有数据则读取的数据导入到内部的PipeStream对象并回调; 接下来看一下如何在Socket中使用SslStreamX.
public void CreateSSL(AsyncCallback asyncCallback, ListenHandler listen, IServer server)
{
try
{
if (server.EnableLog(EventArgs.LogType.Info))
server.Log(EventArgs.LogType.Info, null, $"{RemoteEndPoint} create ssl stream");
mBaseNetStream.SSL = true;
mSslStream = new SslStreamX(this.SendBufferPool, server.Options.Encoding,
server.Options.LittleEndian, mBaseNetStream, false);
mSslStream.BeginAuthenticateAsServer(listen.Certificate, false, listen.SslProtocols,true, new AsyncCallback(asyncCallback),
new Tuple<TcpSession, SslStream>(this, this.mSslStream));
}
catch (Exception e_)
{
if (server.EnableLog(EventArgs.LogType.Warring))
server.Log(EventArgs.LogType.Warring, this, $"{this.RemoteEndPoint} create session ssl error {e_.Message}@{e_.StackTrace}");
this.Dispose();
}
}
(https://github.com/IKende/BeetleX/blob/master/src/BeetleX/Buffers/SslStramX.cs)
以上是组件针对SslStreamX的一个集成代码。