Tôi có một ứng dụng sử dụng SslStream
để gửi và nhận dữ liệu với khung có độ dài cố định của riêng nó. Các dòng được tạo ra bằng cách gói NetworkStream
trở về từ TcpClient.GetStream()
như vậy:Tôi có thể mong đợi mức độ an toàn của luồng nào từ System.Net.Security.SslStream?
var client = new TcpClient();
client.Connect(host, port);
var sslStream = new SslStream(client.GetStream(), false, callback, null);
sslStream.AuthenticateAsClient(hostname);
Bởi vì giao thức là hoàn toàn không đồng bộ (đóng khung "thông điệp" đến vào những thời điểm tùy ý và khách hàng được phép gửi chúng vào những thời điểm tùy ý), tôi bình thường sẽ sinh ra một sợi chịu trách nhiệm chặn trên NetworkStream.Read()
và nếu không đảm bảo rằng chỉ có một chuỗi gọi NetworkStream.Write(...)
tại một thời điểm bất kỳ.
Phần Remarks cho NetworkStream
nói:
Đọc và ghi các hoạt động có thể được thực hiện đồng thời trên một thể hiện của lớp NetworkStream mà không cần đồng bộ hóa. Miễn là có một chuỗi duy nhất cho hoạt động ghi và một chuỗi duy nhất cho hoạt động đọc, sẽ không có sự can thiệp chéo giữa đọc và viết luồng và không cần đồng bộ hóa.
Tuy nhiên, MSDN documentation "Chủ đề an toàn" để biết SslStream
nói:
Bất kỳ public static (chung trong Visual Basic) thành viên thuộc loại này là chủ đề an toàn. Bất kỳ thành viên cá thể nào cũng không được đảm bảo an toàn là an toàn cho các thanh công cụ .
Vì SslStream
và NetworkStream
không nằm trong hệ thống phân cấp cùng lớp, tôi đã giả định (có thể không chính xác) rằng những nhận xét cho NetworkStream
không áp dụng cho SslStream
.
Cách tiếp cận tốt nhất cho an toàn luồng chỉ cần quấn SslStream.BeginRead
/SslStream.EndRead
và SslStream.BeginWrite
/SslStream.EndWrite
với thứ gì đó như thế này?
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();
}
}
Chi tiết nhỏ: khóa trên _stream sẽ hoạt động nhưng bạn nên tạo và sử dụng một đối tượng riêng cho mục đích này. –
@HenkHolterman Chúc mừng cho lời khuyên; Tôi đã cập nhật mã. –
FWIW: [SafeSslStream] (https://github.com/smarkets/IronSmarkets/blob/master/IronSmarkets/Sockets/SafeSslStream.cs) đang được đề cập là dành cho [IronSmarkets] (https://github.com/smarkets)/IronSmarkets) dự án. –