我有一个应用程序使用SslStream
来发送和接收具有自己的固定长度框架的数据。通过包装从NetworkStream
返回的TcpClient.GetStream()
来创建流,如下所示:
var client = new TcpClient();
client.Connect(host, port);
var sslStream = new SslStream(client.GetStream(), false, callback, null);
sslStream.AuthenticateAsClient(hostname);
因为协议是完全异步的(框架“消息”在任意时间到达并且允许客户端在任意时间发送它们),我通常会生成一个负责阻塞NetworkStream.Read()
的线程,否则确保任何时候只有一个线程调用NetworkStream.Write(...)
。
NetworkStream
的{{3}}部分说:
读取和写入操作可以同时执行 不需要NetworkStream类的实例 同步。只要有一个独特的线程 写操作和一个用于读操作的唯一线程, 读写之间不会存在交叉干扰 线程并且不需要同步。
但是,SslStream
的{{3}}“主题安全”部分说明了:
此类型的任何公共静态(在Visual Basic中为Shared)成员 是线程安全的。不保证任何实例成员 线程安全。
由于SslStream
和NetworkStream
不在同一个类层次结构中,我假设(可能不正确)NetworkStream
的评论不适用于SslStream
线程安全的最佳方法是简单地将SslStream.BeginRead
/ SslStream.EndRead
和SslStream.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 :(得分:3)
您从MSDN文档中提取的短语是大多数类文档中的全部内容。如果成员的文档明确说明了线程的安全性(如NetworkStream
那样),那么你可以依赖它。
然而,他们所说的是你可以同时执行一次读取和一次写入,而不是两次读取或两次写入。因此,您需要单独同步或排队读取和写入。您的代码看起来足以完成此任务。