2011-11-01 19 views
7

私はSslStreamを使って独自の固定長フレーミングでデータを送受信するアプリケーションを持っています。私は、System.Net.Security.SslStreamからどのレベルのスレッドセーフが期待できますか?

var client = new TcpClient(); 
client.Connect(host, port); 
var sslStream = new SslStream(client.GetStream(), false, callback, null); 
sslStream.AuthenticateAsClient(hostname); 

をプロトコルが完全に非同期であるため(フレーム「メッセージ」、任意の時間に到着し、クライアントが任意のタイミングでそれらを送信することが許可されている):ストリームがNetworkStreamがそうのようTcpClient.GetStream()から返されたラップによって作成されました通常はNetworkStream.Read()でブロックするスレッドを生成し、それ以外の場合はいつでもNetworkStream.Write(...)を呼び出すスレッドが1つしかないことを確認します。

NetworkStreamためRemarksセクションは言う:

読み取りおよび書き込み操作が 同期を必要とせずにNetworkStreamクラスの インスタンス上で同時に行うことができます。 書き込み操作用に1つのユニークスレッドがあり、読み取り操作に1つのユニークスレッドがある限り、 スレッドの読み取りと書き込みの間に相互干渉はなく、同期は不要です。

しかし、SslStreamためMSDN documentation "スレッドセーフ" セクションは言う:

(Visual BasicではShared)すべてのパブリックstaticこのタイプ のメンバーは、スレッドセーフです。すべてのインスタンスメンバは、 スレッドセーフであることは保証されていません。

SslStreamNetworkStreamは、同じクラス階層に含まれていないので、私は(おそらく間違って)NetworkStreamについての発言がSslStreamには適用されないことを前提としています。

スレッド安全のための最良のアプローチは、単にSslStream.BeginRead/SslStream.EndReadSslStream.BeginWrite/SslStream.EndWriteのようなものですか?

internal sealed class StateObject 
{ 
    private readonly ManualResetEvent _done = new ManualResetEvent(false); 

    public int BytesRead { get; set; } 
    public ManualResetEvent Done { get { return _done; } } 
} 

internal sealed class SafeSslStream 
{ 
    private readonly object _streamLock = new object(); 
    private readonly SslStream _stream; 

    public SafeSslStream(SslStream stream) 
    { 
     _stream = stream; 
    } 

    public int Read(byte[] buffer, int offset, int count) 
    { 
     var state = new StateObject(); 
     lock (_streamLock) 
     { 
      _stream.BeginRead(buffer, offset, count, ReadCallback, state); 
     } 
     state.Done.WaitOne(); 
     return state.BytesRead; 
    } 

    public void Write(byte[] buffer, int offset, int count) 
    { 
     var state = new StateObject(); 
     lock (_streamLock) 
     { 
      _stream.BeginWrite(buffer, offset, count, WriteCallback, state); 
     } 
     state.Done.WaitOne(); 
    } 

    private void ReadCallback(IAsyncResult ar) 
    { 
     var state = (StateObject)ar.AsyncState; 
     lock (_streamLock) 
     { 
      state.BytesRead = _stream.EndRead(ar); 
     } 
     state.Done.Set(); 
    } 

    private void WriteCallback(IAsyncResult ar) 
    { 
     var state = (StateObject)ar.AsyncState; 
     lock (_streamLock) 
     { 
      _stream.EndWrite(ar); 
     } 
     state.Done.Set(); 
    } 
} 
+0

細かいディテール:_streamでロックすることはできますが、この目的のために別のオブジェクトを作成して使用することをお勧めします。 –

+0

@HenkHolterman助言のための乾杯;私はコードを更新しました。 –

+0

FWIW:[SafeSslStream](https://github.com/smarkets/IronSmarkets/blob/master/IronSmarkets/Sockets/SafeSslStream.cs)は、[IronSmarkets](https://github.com/smarkets)用です。/IronSmarkets)プロジェクト。 –

答えて

3

あなたはMSDNのドキュメントから引き出されたフレーズは、キャッチオールほとんどのクラスのドキュメントに配置されています。メンバのドキュメントに明示的にスレッドの安全性が記述されている場合(NetworkStreamのように)、それに頼ることができます。

ただし、2回の読み込みまたは2回の書き込みではなく、1回の読み込みと1回の書き込みを同時に実行できるということです。そのため、読み書きを別々に同期またはキューに入れる必要があります。あなたのコードはこれを行うのに十分であるように見えます。

+0

まだ私には分かりませんが、その注釈は 'NetworkStream'にしか現れず、' SslStream'自体は 'NetworkStream'を継承しませんが、代わりに' TcpClient'と一緒に使用するとラップするようです。 –

+0

あなたのSslStreamはNetworkStreamをラッピングしているので、読み書き操作で暗黙的にスレッドの安全を得ることができます。そうでない場合、SSLは半二重になります(半二重になります)。独自のロックを読み書きするだけで安全です。 – Polynomial

+1

ちょっとした精度を追加するだけです。ほとんどのSSLは全二重ですが、ハンドシェイクには読み書き方向の同期が必要です。クライアントおよびサーバはいつでも再ハンドシェイクを要求することができる。したがって、同期を行わずにあるスレッドで読み取りを行い、もう一方のスレッドで書き込みを行うことは、NetworkStreamのスレッド安全性の明白な結果ではありません。幸いにも、MicrosoftのSslStreamの実装には、このようなバイスレッド操作を可能にするために必要な内部ロックが含まれています(ただし、Monoはチェックしていません)。 –

関連する問題