8

I have an application which uses SslStream to send and receive data with its own fixed-length framing. The stream is created by wrapping the NetworkStream returned from TcpClient.GetStream() like so:

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

Because the protocol is fully asynchronous (framed "messages" arrive at arbitrary times and the client is allowed to send them at arbitrary times), I would normally spawn a thread responsible for blocking on NetworkStream.Read() and otherwise ensure that there is only one thread calling NetworkStream.Write(...) at any one time.

The Remarks section for NetworkStream says:

Read and write operations can be performed simultaneously on an instance of the NetworkStream class without the need for synchronization. As long as there is one unique thread for the write operations and one unique thread for the read operations, there will be no cross-interference between read and write threads and no synchronization is required.

However, the MSDN documentation "Thread Safety" section for SslStream says:

Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

Because SslStream and NetworkStream aren't in the same class hierarchy, I have assumed (perhaps incorrectly) that the remarks for NetworkStream don't apply to SslStream.

Is the best approach for thread safety to simply wrap SslStream.BeginRead / SslStream.EndRead and SslStream.BeginWrite / SslStream.EndWrite with something like this?

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();
    }
}
4

1 に答える 1

4

MSDNのドキュメントから抜粋したフレーズは、ほとんどのクラスのドキュメントに記載されているキャッチオールです。メンバーのドキュメントにスレッドセーフが明示的に記載されている場合(NetworkStreamそうです)、それを信頼できます。

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

于 2011-11-01T17:00:02.033 に答える