我正在为一个遗留的RPC实现制作客户端/服务器库,但遇到了一些问题,当等待接收RPC请求消息的响应消息时,客户端有时会挂起。原来真正的问题是在我的消息帧代码中(当从底层NetworkStream
读取数据时,我没有正确处理消息边界),但它也让我对我用于跨网络发送数据的代码感到怀疑,特别是在RPC服务器向客户端发送大量数据作为客户端RPC请求的结果的情况下。
我的发送代码使用 BinaryWriter
将完整的“消息”写入基础的 NetworkStream
。RPC 协议还实现了一个心跳算法,其中 RPC 服务器每 15 秒发送一次 PING 消息。PING 消息是由一个独立的线程发送的,因此至少在理论上,当服务器正在向客户端流回一个大响应时,可以发送一个 ping。
假设我有以下Send
方法,其中stream
是一个NetworkStream
:
public void Send(Message message)
{
//Write the message to a temporary stream so we can send it all-at-once
MemoryStream tempStream = new MemoryStream();
message.WriteToStream(tempStream);
//Write the serialized message to the stream.
//The BinaryWriter is a little redundant in this
//simplified example, but here because
//the production code uses it.
byte[] data = tempStream.ToArray();
BinaryWriter bw = new BinaryWriter(stream);
bw.Write(data, 0, data.Length);
bw.Flush();
}
我所问的问题是,bw.Write
的调用(以及隐含的底层Stream
s的Write
方法的调用)是原子的吗?也就是说,如果发送线程上仍在进行长时间的Write
,并且心跳线程启动并发送一个PING消息,那么该线程会阻塞直到原始的Write
调用完成,或者我必须在Send
方法中添加显式同步以防止两个Send
调用抵消了流?