Mam aplikację, która używa SslStream
do wysyłania i odbierania danych z własną ramką o stałej długości. Strumień jest tworzony przez owijanie NetworkStream
wrócił z TcpClient.GetStream()
tak:Jakiego poziomu bezpieczeństwa wątku mogę się spodziewać w System.Net.Security.SslStream?
var client = new TcpClient();
client.Connect(host, port);
var sslStream = new SslStream(client.GetStream(), false, callback, null);
sslStream.AuthenticateAsClient(hostname);
Ponieważ protokół jest całkowicie asynchroniczny (oprawione „wiadomości” przybyć w dowolnych godzinach, a klient może wysłać je w dowolnych godzinach), I normalnie spawnuje wątek odpowiedzialny za blokowanie na NetworkStream.Read()
iw inny sposób zapewnia, że istnieje tylko jeden wątek wywołujący NetworkStream.Write(...)
w tym samym czasie.
Sekcja Remarks dla NetworkStream
mówi:
operacje odczytu i zapisu mogą być wykonywane jednocześnie na instancji klasy NetworkStream bez potrzeby synchronizacji. Dopóki istnieje jeden unikalny wątek dla operacji zapisu i jeden unikalny wątek dla operacji odczytu, nie będzie interferencji między wątkami odczytu i zapisu i nie jest wymagana żadna synchronizacja.
Jednak Sekcja MSDN documentation "Bezpieczeństwo wątków" dla SslStream
mówi:
Wszystkie publiczne statyczny (Shared w języku Visual Basic) elementy członkowskie tego typu są bezpieczne dla wątków . Nie można zagwarantować, że żaden element instancji nie będzie bezpieczny dla wątków .
Ponieważ SslStream
i NetworkStream
nie są w tej samej hierarchii klas, mam przyjąć, (być może błędnie), że uwagi dotyczące NetworkStream
nie dotyczą SslStream
.
Czy to jest najlepsze podejście do bezpieczeństwa wątków, aby po prostu owinąć SslStream.BeginRead
/SslStream.EndRead
i SslStream.BeginWrite
/SslStream.EndWrite
z czymś takim?
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();
}
}
Drobne szczegóły: zablokowanie _stream będzie działało, ale zaleca się utworzenie i użycie osobnego obiektu do tego celu. –
@HenkHolterman Wiwaty za poradę; Zaktualizowałem kod. –
FWIW: Dysk [SafeSsLStream] (https://github.com/smarkets/IronSmarkets/blob/master/IronSmarkets/Sockets/SafeSslStream.cs) jest przeznaczony dla [IronSmarkets] (https://github.com/smarkets/IronSmarkets). –