数据加密在通讯环应用是必不可少的,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;//提交数据到SslStreamprivate void OnWriterFlash(Buffers.IBuffer data){StreamHelper.WriteBuffer(this, data);}//提交数据public override void Flush(){if (mPipeStream != null)mPipeStream.Flush();base.Flush();}//获取当前SslStream对应的PipeStreampublic 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的一个集成代码。
